Browse Source

✨ feat: add dark mode detection and styling enhancements to PersonalSetting and TopUp components

CaIon 7 months ago
parent
commit
e4217f64d3
3 changed files with 312 additions and 290 deletions
  1. 95 94
      web/src/components/settings/PersonalSetting.js
  2. 1 2
      web/src/helpers/render.js
  3. 216 194
      web/src/pages/TopUp/index.js

+ 95 - 94
web/src/components/settings/PersonalSetting.js

@@ -104,6 +104,33 @@ const PersonalSetting = () => {
   });
   const [modelsLoading, setModelsLoading] = useState(true);
   const [showWebhookDocs, setShowWebhookDocs] = useState(true);
+  const [isDarkMode, setIsDarkMode] = useState(false);
+
+  // 检测暗色模式
+  useEffect(() => {
+    const checkDarkMode = () => {
+      const isDark = document.documentElement.classList.contains('dark') || 
+                    window.matchMedia('(prefers-color-scheme: dark)').matches;
+      setIsDarkMode(isDark);
+    };
+
+    checkDarkMode();
+    
+    // 监听主题变化
+    const observer = new MutationObserver(checkDarkMode);
+    observer.observe(document.documentElement, {
+      attributes: true,
+      attributeFilter: ['class']
+    });
+
+    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+    mediaQuery.addListener(checkDarkMode);
+
+    return () => {
+      observer.disconnect();
+      mediaQuery.removeListener(checkDarkMode);
+    };
+  }, []);
 
   useEffect(() => {
     let status = localStorage.getItem('status');
@@ -384,107 +411,81 @@ const PersonalSetting = () => {
               <Card className="!rounded-2xl shadow-lg border-0">
                 {/* 顶部用户信息区域 */}
                 <Card
-                  className="!rounded-2xl !border-0 !shadow-2xl overflow-hidden"
+                  className="!rounded-2xl !border-0 !shadow-lg overflow-hidden"
                   style={{
-                    background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 25%, #a855f7 50%, #c084fc 75%, #d8b4fe 100%)',
+                    background: isDarkMode 
+                      ? 'linear-gradient(135deg, #1e293b 0%, #334155 50%, #475569 100%)'
+                      : 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 50%, #cbd5e1 100%)',
                     position: 'relative'
                   }}
                   bodyStyle={{ padding: 0 }}
                 >
                   {/* 装饰性背景元素 */}
                   <div className="absolute inset-0 overflow-hidden">
-                    <div className="absolute -top-10 -right-10 w-40 h-40 bg-white opacity-5 rounded-full"></div>
-                    <div className="absolute -bottom-16 -left-16 w-48 h-48 bg-white opacity-3 rounded-full"></div>
-                    <div className="absolute top-1/2 right-1/4 w-24 h-24 bg-yellow-400 opacity-10 rounded-full"></div>
+                    <div className="absolute -top-10 -right-10 w-40 h-40 bg-slate-400 dark:bg-slate-500 opacity-5 rounded-full"></div>
+                    <div className="absolute -bottom-16 -left-16 w-48 h-48 bg-slate-300 dark:bg-slate-400 opacity-8 rounded-full"></div>
+                    <div className="absolute top-1/2 right-1/4 w-24 h-24 bg-slate-400 dark:bg-slate-500 opacity-6 rounded-full"></div>
                   </div>
 
-                  <div className="relative p-4 sm:p-6 md:p-8" style={{ color: 'white' }}>
+                  <div className="relative p-4 sm:p-6 md:p-8 text-gray-600 dark:text-gray-300">
                     <div className="flex justify-between items-start mb-4 sm:mb-6">
                       <div className="flex items-center flex-1 min-w-0">
                         <Avatar
                           size='large'
-                          color={stringToColor(getUsername())}
-                          border={{ motion: true }}
-                          contentMotion={true}
-                          className="mr-3 sm:mr-4 shadow-lg flex-shrink-0"
+                          className="mr-3 sm:mr-4 shadow-md flex-shrink-0 bg-slate-500 dark:bg-slate-400"
                         >
                           {getAvatarText()}
                         </Avatar>
                         <div className="flex-1 min-w-0">
-                          <div className="text-base sm:text-lg font-semibold truncate" style={{ color: 'white' }}>
+                          <div className="text-base sm:text-lg font-semibold truncate text-gray-800 dark:text-gray-100">
                             {getUsername()}
                           </div>
                           <div className="mt-1 flex flex-wrap gap-1 sm:gap-2">
                             {isRoot() ? (
                               <Tag
-                                color='red'
                                 size='small'
-                                style={{
-                                  backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                  color: '#dc2626',
-                                  fontWeight: '600'
-                                }}
-                                className="!rounded-full"
+                                className="!rounded-full bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border border-gray-200 dark:border-gray-600"
+                                style={{ fontWeight: '500' }}
                               >
                                 {t('超级管理员')}
                               </Tag>
                             ) : isAdmin() ? (
                               <Tag
-                                color='orange'
                                 size='small'
-                                style={{
-                                  backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                  color: '#ea580c',
-                                  fontWeight: '600'
-                                }}
-                                className="!rounded-full"
+                                className="!rounded-full bg-gray-50 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border border-gray-200 dark:border-gray-600"
+                                style={{ fontWeight: '500' }}
                               >
                                 {t('管理员')}
                               </Tag>
                             ) : (
                               <Tag
-                                color='blue'
                                 size='small'
-                                style={{
-                                  backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                  color: '#2563eb',
-                                  fontWeight: '600'
-                                }}
-                                className="!rounded-full"
+                                className="!rounded-full bg-slate-50 dark:bg-slate-700 text-slate-600 dark:text-slate-300 border border-slate-200 dark:border-slate-600"
+                                style={{ fontWeight: '500' }}
                               >
                                 {t('普通用户')}
                               </Tag>
                             )}
                             <Tag
-                              color='green'
                               size='small'
-                              className="!rounded-full"
-                              style={{
-                                backgroundColor: 'rgba(255, 255, 255, 0.95)',
-                                color: '#16a34a',
-                                fontWeight: '600'
-                              }}
+                              className="!rounded-full bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300 border border-slate-200 dark:border-slate-600"
+                              style={{ fontWeight: '500' }}
                             >
                               ID: {userState?.user?.id}
                             </Tag>
                           </div>
                         </div>
                       </div>
-                      <div
-                        className="w-10 h-10 sm:w-12 sm:h-12 rounded-lg flex items-center justify-center shadow-lg flex-shrink-0 ml-2"
-                        style={{
-                          background: `linear-gradient(135deg, ${stringToColor(getUsername())} 0%, #f59e0b 100%)`
-                        }}
-                      >
-                        <IconUser size="default" style={{ color: 'white' }} />
+                      <div className="w-10 h-10 sm:w-12 sm:h-12 rounded-lg flex items-center justify-center shadow-md flex-shrink-0 ml-2 bg-slate-400 dark:bg-slate-500">
+                        <IconUser size="default" className="text-white" />
                       </div>
                     </div>
 
                     <div className="mb-4 sm:mb-6">
-                      <div className="text-xs sm:text-sm mb-1 sm:mb-2" style={{ color: 'rgba(255, 255, 255, 0.7)' }}>
+                      <div className="text-xs sm:text-sm mb-1 sm:mb-2 text-gray-500 dark:text-gray-400">
                         {t('当前余额')}
                       </div>
-                      <div className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-wide" style={{ color: 'white' }}>
+                      <div className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-wide text-gray-900 dark:text-gray-100">
                         {renderQuota(userState?.user?.quota)}
                       </div>
                     </div>
@@ -492,33 +493,33 @@ const PersonalSetting = () => {
                     <div className="flex flex-col sm:flex-row sm:justify-between sm:items-end">
                       <div className="grid grid-cols-3 gap-2 sm:flex sm:space-x-6 lg:space-x-8 mb-3 sm:mb-0">
                         <div className="text-center sm:text-left">
-                          <div className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.6)' }}>
+                          <div className="text-xs text-gray-400 dark:text-gray-500">
                             {t('历史消耗')}
                           </div>
-                          <div className="text-xs sm:text-sm font-medium truncate" style={{ color: 'white' }}>
+                          <div className="text-xs sm:text-sm font-medium truncate text-gray-600 dark:text-gray-300">
                             {renderQuota(userState?.user?.used_quota)}
                           </div>
                         </div>
                         <div className="text-center sm:text-left">
-                          <div className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.6)' }}>
+                          <div className="text-xs text-gray-400 dark:text-gray-500">
                             {t('请求次数')}
                           </div>
-                          <div className="text-xs sm:text-sm font-medium truncate" style={{ color: 'white' }}>
+                          <div className="text-xs sm:text-sm font-medium truncate text-gray-600 dark:text-gray-300">
                             {userState.user?.request_count || 0}
                           </div>
                         </div>
                         <div className="text-center sm:text-left">
-                          <div className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.6)' }}>
+                          <div className="text-xs text-gray-400 dark:text-gray-500">
                             {t('用户分组')}
                           </div>
-                          <div className="text-xs sm:text-sm font-medium truncate" style={{ color: 'white' }}>
+                          <div className="text-xs sm:text-sm font-medium truncate text-gray-600 dark:text-gray-300">
                             {userState?.user?.group || t('默认')}
                           </div>
                         </div>
                       </div>
                     </div>
 
-                    <div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-yellow-400 via-orange-400 to-red-400" style={{ opacity: 0.6 }}></div>
+                    <div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-slate-300 via-slate-400 to-slate-500 dark:from-slate-600 dark:via-slate-500 dark:to-slate-400 opacity-40"></div>
                   </div>
                 </Card>
 
@@ -537,10 +538,10 @@ const PersonalSetting = () => {
                     >
                       <div className="gap-6 py-4">
                         {/* 可用模型部分 */}
-                        <div className="bg-gray-50 rounded-xl">
+                        <div className="bg-gray-50 dark:bg-gray-800 rounded-xl">
                           <div className="flex items-center mb-4">
-                            <div className="w-10 h-10 rounded-full bg-purple-50 flex items-center justify-center mr-3">
-                              <Settings size={20} className="text-purple-500" />
+                            <div className="w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3">
+                              <Settings size={20} className="text-slate-600 dark:text-slate-300" />
                             </div>
                             <div>
                               <Typography.Title heading={6} className="mb-0">{t('模型列表')}</Typography.Title>
@@ -629,7 +630,7 @@ const PersonalSetting = () => {
                                 </Tabs>
                               </div>
 
-                              <div className="bg-white rounded-lg p-3">
+                              <div className="bg-white dark:bg-gray-700 rounded-lg p-3">
                                 {(() => {
                                   // 根据当前选中的分类过滤模型
                                   const categories = getModelCategories(t);
@@ -736,9 +737,9 @@ const PersonalSetting = () => {
                             shadows='hover'
                           >
                             <div className="flex items-center justify-between">
-                              <div className="flex items-center flex-1">
-                                <div className="w-10 h-10 rounded-full bg-red-50 flex items-center justify-center mr-3">
-                                  <IconMail size="default" className="text-red-500" />
+                                                              <div className="flex items-center flex-1">
+                                <div className="w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3">
+                                  <IconMail size="default" className="text-slate-600 dark:text-slate-300" />
                                 </div>
                                 <div className="flex-1 min-w-0">
                                   <div className="font-medium text-gray-900">{t('邮箱')}</div>
@@ -771,8 +772,8 @@ const PersonalSetting = () => {
                           >
                             <div className="flex items-center justify-between">
                               <div className="flex items-center flex-1">
-                                <div className="w-10 h-10 rounded-full bg-green-50 flex items-center justify-center mr-3">
-                                  <SiWechat size={20} className="text-green-500" />
+                                <div className="w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3">
+                                  <SiWechat size={20} className="text-slate-600 dark:text-slate-300" />
                                 </div>
                                 <div className="flex-1 min-w-0">
                                   <div className="font-medium text-gray-900">{t('微信')}</div>
@@ -808,8 +809,8 @@ const PersonalSetting = () => {
                           >
                             <div className="flex items-center justify-between">
                               <div className="flex items-center flex-1">
-                                <div className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center mr-3">
-                                  <IconGithubLogo size="default" className="text-gray-700" />
+                                <div className="w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3">
+                                  <IconGithubLogo size="default" className="text-slate-600 dark:text-slate-300" />
                                 </div>
                                 <div className="flex-1 min-w-0">
                                   <div className="font-medium text-gray-900">{t('GitHub')}</div>
@@ -844,8 +845,8 @@ const PersonalSetting = () => {
                           >
                             <div className="flex items-center justify-between">
                               <div className="flex items-center flex-1">
-                                <div className="w-10 h-10 rounded-full bg-indigo-50 flex items-center justify-center mr-3">
-                                  <IconShield size="default" className="text-indigo-500" />
+                                <div className="w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3">
+                                  <IconShield size="default" className="text-slate-600 dark:text-slate-300" />
                                 </div>
                                 <div className="flex-1 min-w-0">
                                   <div className="font-medium text-gray-900">{t('OIDC')}</div>
@@ -883,8 +884,8 @@ const PersonalSetting = () => {
                           >
                             <div className="flex items-center justify-between">
                               <div className="flex items-center flex-1">
-                                <div className="w-10 h-10 rounded-full bg-blue-50 flex items-center justify-center mr-3">
-                                  <SiTelegram size={20} className="text-blue-500" />
+                                <div className="w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3">
+                                  <SiTelegram size={20} className="text-slate-600 dark:text-slate-300" />
                                 </div>
                                 <div className="flex-1 min-w-0">
                                   <div className="font-medium text-gray-900">{t('Telegram')}</div>
@@ -926,8 +927,8 @@ const PersonalSetting = () => {
                           >
                             <div className="flex items-center justify-between">
                               <div className="flex items-center flex-1">
-                                <div className="w-10 h-10 rounded-full bg-orange-50 flex items-center justify-center mr-3">
-                                  <SiLinux size={20} className="text-orange-500" />
+                                <div className="w-10 h-10 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-3">
+                                  <SiLinux size={20} className="text-slate-600 dark:text-slate-300" />
                                 </div>
                                 <div className="flex-1 min-w-0">
                                   <div className="font-medium text-gray-900">{t('LinuxDO')}</div>
@@ -978,8 +979,8 @@ const PersonalSetting = () => {
                             >
                               <div className="flex flex-col sm:flex-row items-start sm:justify-between gap-4">
                                 <div className="flex items-start w-full sm:w-auto">
-                                  <div className="w-12 h-12 rounded-full bg-blue-50 flex items-center justify-center mr-4 flex-shrink-0">
-                                    <IconKey size="large" className="text-blue-500" />
+                                  <div className="w-12 h-12 rounded-full bg-slate-100 flex items-center justify-center mr-4 flex-shrink-0">
+                                    <IconKey size="large" className="text-slate-600" />
                                   </div>
                                   <div className="flex-1">
                                     <Typography.Title heading={6} className="mb-1">
@@ -1006,7 +1007,7 @@ const PersonalSetting = () => {
                                   type="primary"
                                   theme="solid"
                                   onClick={generateAccessToken}
-                                  className="!rounded-lg !bg-blue-500 hover:!bg-blue-600 w-full sm:w-auto"
+                                  className="!rounded-lg !bg-slate-600 hover:!bg-slate-700 w-full sm:w-auto"
                                   icon={<IconKey />}
                                 >
                                   {systemToken ? t('重新生成') : t('生成令牌')}
@@ -1022,8 +1023,8 @@ const PersonalSetting = () => {
                             >
                               <div className="flex flex-col sm:flex-row items-start sm:justify-between gap-4">
                                 <div className="flex items-start w-full sm:w-auto">
-                                  <div className="w-12 h-12 rounded-full bg-orange-50 flex items-center justify-center mr-4 flex-shrink-0">
-                                    <IconLock size="large" className="text-orange-500" />
+                                  <div className="w-12 h-12 rounded-full bg-slate-100 flex items-center justify-center mr-4 flex-shrink-0">
+                                    <IconLock size="large" className="text-slate-600" />
                                   </div>
                                   <div>
                                     <Typography.Title heading={6} className="mb-1">
@@ -1038,7 +1039,7 @@ const PersonalSetting = () => {
                                   type="primary"
                                   theme="solid"
                                   onClick={() => setShowChangePasswordModal(true)}
-                                  className="!rounded-lg !bg-orange-500 hover:!bg-orange-600 w-full sm:w-auto"
+                                  className="!rounded-lg !bg-slate-600 hover:!bg-slate-700 w-full sm:w-auto"
                                   icon={<IconLock />}
                                 >
                                   {t('修改密码')}
@@ -1054,11 +1055,11 @@ const PersonalSetting = () => {
                             >
                               <div className="flex flex-col sm:flex-row items-start sm:justify-between gap-4">
                                 <div className="flex items-start w-full sm:w-auto">
-                                  <div className="w-12 h-12 rounded-full bg-red-50 flex items-center justify-center mr-4 flex-shrink-0">
-                                    <IconDelete size="large" className="text-red-500" />
+                                  <div className="w-12 h-12 rounded-full bg-slate-100 flex items-center justify-center mr-4 flex-shrink-0">
+                                    <IconDelete size="large" className="text-slate-600" />
                                   </div>
                                   <div>
-                                    <Typography.Title heading={6} className="mb-1 text-red-600">
+                                    <Typography.Title heading={6} className="mb-1 text-slate-700">
                                       {t('删除账户')}
                                     </Typography.Title>
                                     <Typography.Text type="tertiary" className="text-sm">
@@ -1070,7 +1071,7 @@ const PersonalSetting = () => {
                                   type="danger"
                                   theme="solid"
                                   onClick={() => setShowAccountDeleteModal(true)}
-                                  className="!rounded-lg w-full sm:w-auto"
+                                  className="!rounded-lg w-full sm:w-auto !bg-slate-500 hover:!bg-slate-600"
                                   icon={<IconDelete />}
                                 >
                                   {t('删除账户')}
@@ -1111,7 +1112,7 @@ const PersonalSetting = () => {
                                 >
                                   <Radio value='email' className="!p-4 !rounded-lg">
                                     <div className="flex items-center">
-                                      <IconMail className="mr-2 text-blue-500" />
+                                      <IconMail className="mr-2 text-slate-600" />
                                       <div>
                                         <div className="font-medium">{t('邮件通知')}</div>
                                         <div className="text-sm text-gray-500">{t('通过邮件接收通知')}</div>
@@ -1120,7 +1121,7 @@ const PersonalSetting = () => {
                                   </Radio>
                                   <Radio value='webhook' className="!p-4 !rounded-lg">
                                     <div className="flex items-center">
-                                      <Webhook size={16} className="mr-2 text-green-500" />
+                                      <Webhook size={16} className="mr-2 text-slate-600" />
                                       <div>
                                         <div className="font-medium">{t('Webhook通知')}</div>
                                         <div className="text-sm text-gray-500">{t('通过HTTP请求接收通知')}</div>
@@ -1167,11 +1168,11 @@ const PersonalSetting = () => {
                                     </div>
                                   </div>
 
-                                  <div className="bg-yellow-50 rounded-xl">
+                                  <div className="bg-slate-50 rounded-xl">
                                     <div className="flex items-center justify-between cursor-pointer" onClick={() => setShowWebhookDocs(!showWebhookDocs)}>
                                       <div className="flex items-center">
-                                        <Globe size={16} className="mr-2 text-yellow-600" />
-                                        <Typography.Text strong className="text-yellow-800">
+                                        <Globe size={16} className="mr-2 text-slate-600" />
+                                        <Typography.Text strong className="text-slate-700">
                                           {t('Webhook请求结构')}
                                         </Typography.Text>
                                       </div>
@@ -1254,11 +1255,11 @@ const PersonalSetting = () => {
                             itemKey='price'
                           >
                             <div className="py-4">
-                              <div className="bg-white rounded-xl">
-                                <div className="flex items-start">
-                                  <div className="w-10 h-10 rounded-full bg-orange-50 flex items-center justify-center mt-1">
-                                    <Shield size={20} className="text-orange-500" />
-                                  </div>
+                                                              <div className="bg-white rounded-xl">
+                                  <div className="flex items-start">
+                                    <div className="w-10 h-10 rounded-full bg-slate-100 flex items-center justify-center mt-1">
+                                      <Shield size={20} className="text-slate-600" />
+                                    </div>
                                   <div className="flex-1">
                                     <div className="flex items-center justify-between">
                                       <div>
@@ -1292,7 +1293,7 @@ const PersonalSetting = () => {
                             type='primary'
                             onClick={saveNotificationSettings}
                             size="large"
-                            className="!rounded-lg !bg-purple-500 hover:!bg-purple-600"
+                            className="!rounded-lg !bg-slate-600 hover:!bg-slate-700"
                             icon={<IconSetting />}
                           >
                             {t('保存设置')}
@@ -1408,7 +1409,7 @@ const PersonalSetting = () => {
             theme="solid"
             size='large'
             onClick={bindWeChat}
-            className="!rounded-lg w-full !bg-green-500 hover:!bg-green-600"
+            className="!rounded-lg w-full !bg-slate-600 hover:!bg-slate-700"
             icon={<SiWechat size={16} />}
           >
             {t('绑定')}

+ 1 - 2
web/src/helpers/render.js

@@ -580,7 +580,7 @@ export function renderText(text, limit) {
 export function renderGroup(group) {
   if (group === '') {
     return (
-      <Tag size='large' key='default' color='orange' shape='circle' prefixIcon={<Users size={14} />}>
+      <Tag size='large' key='default' color='orange' shape='circle'>
         {i18next.t('用户分组')}
       </Tag>
     );
@@ -603,7 +603,6 @@ export function renderGroup(group) {
           color={tagColors[group] || stringToColor(group)}
           key={group}
           shape='circle'
-          prefixIcon={<Users size={14} />}
           onClick={async (event) => {
             event.stopPropagation();
             if (await copy(group)) {

+ 216 - 194
web/src/pages/TopUp/index.js

@@ -55,6 +55,7 @@ const TopUp = () => {
   const [amountLoading, setAmountLoading] = useState(false);
   const [paymentLoading, setPaymentLoading] = useState(false);
   const [confirmLoading, setConfirmLoading] = useState(false);
+  const [isDarkMode, setIsDarkMode] = useState(false);
 
   // 邀请相关状态
   const [affLink, setAffLink] = useState('');
@@ -256,6 +257,32 @@ const TopUp = () => {
     showSuccess(t('邀请链接已复制到剪切板'));
   };
 
+  // 检测暗色模式
+  useEffect(() => {
+    const checkDarkMode = () => {
+      const isDark = document.documentElement.classList.contains('dark') || 
+                    window.matchMedia('(prefers-color-scheme: dark)').matches;
+      setIsDarkMode(isDark);
+    };
+
+    checkDarkMode();
+    
+    // 监听主题变化
+    const observer = new MutationObserver(checkDarkMode);
+    observer.observe(document.documentElement, {
+      attributes: true,
+      attributeFilter: ['class']
+    });
+
+    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
+    mediaQuery.addListener(checkDarkMode);
+
+    return () => {
+      observer.disconnect();
+      mediaQuery.removeListener(checkDarkMode);
+    };
+  }, []);
+
   useEffect(() => {
     if (userState?.user?.id) {
       setUserDataLoading(false);
@@ -398,48 +425,45 @@ const TopUp = () => {
             <div className="w-full">
               <Card className="!rounded-2xl shadow-lg border-0">
                 <Card
-                  className="!rounded-2xl !border-0 !shadow-2xl overflow-hidden"
+                  className="!rounded-2xl !border-0 !shadow-lg overflow-hidden"
                   style={{
-                    background: 'linear-gradient(135deg, #1e3a8a 0%, #1e40af 25%, #2563eb 50%, #3b82f6 75%, #60a5fa 100%)',
+                    background: isDarkMode 
+                      ? 'linear-gradient(135deg, #1e293b 0%, #334155 50%, #475569 100%)'
+                      : 'linear-gradient(135deg, #f8fafc 0%, #e2e8f0 50%, #cbd5e1 100%)',
                     position: 'relative'
                   }}
                   bodyStyle={{ padding: 0 }}
                 >
                   <div className="absolute inset-0 overflow-hidden">
-                    <div className="absolute -top-10 -right-10 w-40 h-40 bg-white opacity-5 rounded-full"></div>
-                    <div className="absolute -bottom-16 -left-16 w-48 h-48 bg-white opacity-3 rounded-full"></div>
-                    <div className="absolute top-1/2 right-1/4 w-24 h-24 bg-yellow-400 opacity-10 rounded-full"></div>
+                    <div className="absolute -top-10 -right-10 w-40 h-40 bg-slate-400 opacity-5 rounded-full"></div>
+                    <div className="absolute -bottom-16 -left-16 w-48 h-48 bg-slate-300 opacity-8 rounded-full"></div>
+                    <div className="absolute top-1/2 right-1/4 w-24 h-24 bg-slate-400 opacity-6 rounded-full"></div>
                   </div>
 
-                  <div className="relative p-4 sm:p-6 md:p-8" style={{ color: 'white' }}>
+                  <div className="relative p-4 sm:p-6 md:p-8 text-gray-600 dark:text-gray-300">
                     <div className="flex justify-between items-start mb-4 sm:mb-6">
                       <div className="flex-1 min-w-0">
                         {userDataLoading ? (
                           <Skeleton.Title style={{ width: '200px', height: '20px' }} />
                         ) : (
-                          <div className="text-base sm:text-lg font-semibold truncate" style={{ color: 'white' }}>
+                          <div className="text-base sm:text-lg font-semibold truncate text-gray-800 dark:text-gray-100">
                             {t('尊敬的')} {getUsername()}
                           </div>
                         )}
                       </div>
-                      <div
-                        className="w-10 h-10 sm:w-12 sm:h-12 rounded-lg flex items-center justify-center shadow-lg flex-shrink-0 ml-2"
-                        style={{
-                          background: `linear-gradient(135deg, ${stringToColor(getUsername())} 0%, #f59e0b 100%)`
-                        }}
-                      >
-                        <IconCreditCard size="default" style={{ color: 'white' }} />
+                      <div className="w-10 h-10 sm:w-12 sm:h-12 rounded-lg flex items-center justify-center shadow-md flex-shrink-0 ml-2 bg-slate-400 dark:bg-slate-500">
+                        <IconCreditCard size="default" className="text-white" />
                       </div>
                     </div>
 
                     <div className="mb-4 sm:mb-6">
-                      <div className="text-xs sm:text-sm mb-1 sm:mb-2" style={{ color: 'rgba(255, 255, 255, 0.7)' }}>
+                      <div className="text-xs sm:text-sm mb-1 sm:mb-2 text-gray-500 dark:text-gray-400">
                         {t('当前余额')}
                       </div>
                       {userDataLoading ? (
                         <Skeleton.Title style={{ width: '180px', height: '32px' }} />
                       ) : (
-                        <div className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-wide" style={{ color: 'white' }}>
+                        <div className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-wide text-gray-900 dark:text-gray-100">
                           {renderQuota(userState?.user?.quota || userQuota)}
                         </div>
                       )}
@@ -448,37 +472,37 @@ const TopUp = () => {
                     <div className="flex flex-col sm:flex-row sm:justify-between sm:items-end">
                       <div className="grid grid-cols-3 gap-2 sm:flex sm:space-x-6 lg:space-x-8 mb-3 sm:mb-0">
                         <div className="text-center sm:text-left">
-                          <div className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.6)' }}>
+                          <div className="text-xs text-gray-400 dark:text-gray-500">
                             {t('历史消耗')}
                           </div>
                           {userDataLoading ? (
                             <Skeleton.Title style={{ width: '60px', height: '14px' }} />
                           ) : (
-                            <div className="text-xs sm:text-sm font-medium truncate" style={{ color: 'white' }}>
+                            <div className="text-xs sm:text-sm font-medium truncate text-gray-600 dark:text-gray-300">
                               {renderQuota(userState?.user?.used_quota || 0)}
                             </div>
                           )}
                         </div>
                         <div className="text-center sm:text-left">
-                          <div className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.6)' }}>
+                          <div className="text-xs text-gray-400 dark:text-gray-500">
                             {t('用户分组')}
                           </div>
                           {userDataLoading ? (
                             <Skeleton.Title style={{ width: '50px', height: '14px' }} />
                           ) : (
-                            <div className="text-xs sm:text-sm font-medium truncate" style={{ color: 'white' }}>
+                            <div className="text-xs sm:text-sm font-medium truncate text-gray-600 dark:text-gray-300">
                               {userState?.user?.group || t('默认')}
                             </div>
                           )}
                         </div>
                         <div className="text-center sm:text-left">
-                          <div className="text-xs" style={{ color: 'rgba(255, 255, 255, 0.6)' }}>
+                          <div className="text-xs text-gray-400 dark:text-gray-500">
                             {t('用户角色')}
                           </div>
                           {userDataLoading ? (
                             <Skeleton.Title style={{ width: '60px', height: '14px' }} />
                           ) : (
-                            <div className="text-xs sm:text-sm font-medium truncate" style={{ color: 'white' }}>
+                            <div className="text-xs sm:text-sm font-medium truncate text-gray-600 dark:text-gray-300">
                               {getUserRole()}
                             </div>
                           )}
@@ -489,32 +513,187 @@ const TopUp = () => {
                         {userDataLoading ? (
                           <Skeleton.Title style={{ width: '50px', height: '24px' }} />
                         ) : (
-                          <div
-                            className="px-2 py-1 sm:px-3 rounded-md text-xs sm:text-sm font-medium inline-block"
-                            style={{
-                              backgroundColor: 'rgba(255, 255, 255, 0.2)',
-                              color: 'white',
-                              backdropFilter: 'blur(10px)'
-                            }}
-                          >
+                          <div className="px-2 py-1 sm:px-3 rounded-md text-xs sm:text-sm font-medium inline-block bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300 border border-slate-200 dark:border-slate-600">
                             ID: {userState?.user?.id || '---'}
                           </div>
                         )}
                       </div>
                     </div>
 
-                    <div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-yellow-400 via-orange-400 to-red-400" style={{ opacity: 0.6 }}></div>
+                    <div className="absolute top-0 left-0 w-full h-2 bg-gradient-to-r from-slate-300 via-slate-400 to-slate-500 dark:from-slate-600 dark:via-slate-500 dark:to-slate-400 opacity-40"></div>
                   </div>
                 </Card>
 
                 <div className="p-6">
                   <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
-                    {/* 邀请信息部分 */}
-                    <div>
+                    {/* 左侧:在线充值和兑换余额 */}
+                    <div className="lg:col-span-2 space-y-8">
+                      {/* 在线充值部分 */}
+                      <div>
+                        <div className="flex items-center mb-6">
+                          <div className="w-12 h-12 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-4">
+                            <IconPlus size="large" className="text-slate-600 dark:text-slate-300" />
+                          </div>
+                          <div>
+                            <Text className="text-xl font-semibold">{t('在线充值')}</Text>
+                            <div className="text-gray-500 text-sm">{t('支持多种支付方式')}</div>
+                          </div>
+                        </div>
+
+                        <div className="space-y-4">
+                          <div>
+                            <div className="flex justify-between mb-2">
+                              <Text strong>{t('充值数量')}</Text>
+                              {amountLoading ? (
+                                <Skeleton.Title style={{ width: '80px', height: '14px' }} />
+                              ) : (
+                                <Text type="tertiary">{t('实付金额:') + ' ' + renderAmount()}</Text>
+                              )}
+                            </div>
+                            <InputNumber
+                              disabled={!enableOnlineTopUp}
+                              placeholder={
+                                t('充值数量,最低 ') + renderQuotaWithAmount(minTopUp)
+                              }
+                              value={topUpCount}
+                              min={minTopUp}
+                              max={999999999}
+                              step={1}
+                              precision={0}
+                              onChange={async (value) => {
+                                if (value && value >= 1) {
+                                  setTopUpCount(value);
+                                  await getAmount(value);
+                                }
+                              }}
+                              onBlur={(e) => {
+                                const value = parseInt(e.target.value);
+                                if (!value || value < 1) {
+                                  setTopUpCount(1);
+                                  getAmount(1);
+                                }
+                              }}
+                              size="large"
+                              className="!rounded-lg w-full"
+                              prefix={<IconCreditCard />}
+                              formatter={(value) => value ? `${value}` : ''}
+                              parser={(value) => value ? parseInt(value.replace(/[^\d]/g, '')) : 0}
+                            />
+                          </div>
+
+                          <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
+                            <Button
+                              type="primary"
+                              theme="solid"
+                              onClick={async () => {
+                                preTopUp('zfb');
+                              }}
+                              size="large"
+                              className="!rounded-lg !bg-slate-600 hover:!bg-slate-700 h-14"
+                              disabled={!enableOnlineTopUp}
+                              loading={paymentLoading}
+                              icon={<SiAlipay size={20} />}
+                            >
+                              <span className="ml-2">{t('支付宝')}</span>
+                            </Button>
+                            <Button
+                              type="primary"
+                              theme="solid"
+                              onClick={async () => {
+                                preTopUp('wx');
+                              }}
+                              size="large"
+                              className="!rounded-lg !bg-slate-600 hover:!bg-slate-700 h-14"
+                              disabled={!enableOnlineTopUp}
+                              loading={paymentLoading}
+                              icon={<SiWechat size={20} />}
+                            >
+                              <span className="ml-2">{t('微信')}</span>
+                            </Button>
+                          </div>
+
+                          {!enableOnlineTopUp && (
+                            <Banner
+                              fullMode={false}
+                              type="warning"
+                              icon={null}
+                              closeIcon={null}
+                              className="!rounded-lg"
+                              title={
+                                <div style={{ fontWeight: 600, fontSize: '14px', lineHeight: '20px' }}>
+                                  {t('在线充值功能未开启')}
+                                </div>
+                              }
+                              description={
+                                <div>
+                                  {t('管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。')}
+                                </div>
+                              }
+                            />
+                          )}
+                        </div>
+                      </div>
+
+                      {/* 兑换余额部分 */}
+                      <div>
+                        <div className="flex items-center mb-6">
+                          <div className="w-12 h-12 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-4">
+                            <IconGift size="large" className="text-slate-600 dark:text-slate-300" />
+                          </div>
+                          <div>
+                            <Text className="text-xl font-semibold">{t('兑换余额')}</Text>
+                            <div className="text-gray-500 text-sm">{t('使用兑换码充值余额')}</div>
+                          </div>
+                        </div>
+
+                        <div className="space-y-4">
+                          <div>
+                            <Text strong className="block mb-2">{t('兑换码')}</Text>
+                            <Input
+                              placeholder={t('请输入兑换码')}
+                              value={redemptionCode}
+                              onChange={(value) => setRedemptionCode(value)}
+                              size="large"
+                              className="!rounded-lg"
+                              prefix={<IconGift />}
+                            />
+                          </div>
+
+                          <div className="flex flex-col sm:flex-row gap-3">
+                            {topUpLink && (
+                              <Button
+                                type="primary"
+                                theme="solid"
+                                onClick={openTopUpLink}
+                                size="large"
+                                className="!rounded-lg flex-1"
+                                icon={<IconLink />}
+                              >
+                                {t('获取兑换码')}
+                              </Button>
+                            )}
+                            <Button
+                              type="warning"
+                              theme="solid"
+                              onClick={topUp}
+                              disabled={isSubmitting}
+                              loading={isSubmitting}
+                              size="large"
+                              className="!rounded-lg flex-1"
+                            >
+                              {isSubmitting ? t('兑换中...') : t('兑换')}
+                            </Button>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+
+                    {/* 右侧:邀请信息部分 */}
+                    <div className="lg:col-span-1">
                       <div className="flex items-center justify-between mb-6">
                         <div className="flex items-center">
-                          <div className="w-12 h-12 rounded-full bg-orange-50 flex items-center justify-center mr-4">
-                            <IconLink size="large" className="text-orange-500" />
+                          <div className="w-12 h-12 rounded-full bg-slate-100 dark:bg-slate-700 flex items-center justify-center mr-4">
+                            <IconLink size="large" className="text-slate-600 dark:text-slate-300" />
                           </div>
                           <div>
                             <div className="flex items-center gap-3">
@@ -524,7 +703,7 @@ const TopUp = () => {
                                 theme="solid"
                                 onClick={() => setOpenTransfer(true)}
                                 size="small"
-                                className="!rounded-lg !bg-blue-500 hover:!bg-blue-600"
+                                className="!rounded-lg !bg-slate-600 hover:!bg-slate-700"
                                 icon={<IconCreditCard />}
                               >
                                 {t('划转')}
@@ -536,7 +715,7 @@ const TopUp = () => {
                       </div>
 
                       <div className="space-y-4">
-                        <div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
+                        <div className="grid grid-cols-1 gap-3">
                           <Card
                             className="!rounded-2xl text-center"
                             bodyStyle={{ padding: '16px' }}
@@ -546,7 +725,6 @@ const TopUp = () => {
                             <div className="text-gray-900 text-lg font-bold mt-1">
                               {renderQuota(userState?.user?.aff_quota)}
                             </div>
-
                           </Card>
                           <Card
                             className="!rounded-2xl text-center"
@@ -583,162 +761,6 @@ const TopUp = () => {
                         </div>
                       </div>
                     </div>
-                    <div>
-                      <div className="flex items-center mb-6">
-                        <div className="w-12 h-12 rounded-full bg-green-50 flex items-center justify-center mr-4">
-                          <IconGift size="large" className="text-green-500" />
-                        </div>
-                        <div>
-                          <Text className="text-xl font-semibold">{t('兑换余额')}</Text>
-                          <div className="text-gray-500 text-sm">{t('使用兑换码充值余额')}</div>
-                        </div>
-                      </div>
-
-                      <div className="space-y-4">
-                        <div>
-                          <Text strong className="block mb-2">{t('兑换码')}</Text>
-                          <Input
-                            placeholder={t('请输入兑换码')}
-                            value={redemptionCode}
-                            onChange={(value) => setRedemptionCode(value)}
-                            size="large"
-                            className="!rounded-lg"
-                            prefix={<IconGift />}
-                          />
-                        </div>
-
-                        <div className="flex flex-col sm:flex-row gap-3">
-                          {topUpLink && (
-                            <Button
-                              type="primary"
-                              theme="solid"
-                              onClick={openTopUpLink}
-                              size="large"
-                              className="!rounded-lg flex-1"
-                              icon={<IconLink />}
-                            >
-                              {t('获取兑换码')}
-                            </Button>
-                          )}
-                          <Button
-                            type="warning"
-                            theme="solid"
-                            onClick={topUp}
-                            disabled={isSubmitting}
-                            loading={isSubmitting}
-                            size="large"
-                            className="!rounded-lg flex-1"
-                          >
-                            {isSubmitting ? t('兑换中...') : t('兑换')}
-                          </Button>
-                        </div>
-                      </div>
-                    </div>
-
-                    <div>
-                      <div className="flex items-center mb-6">
-                        <div className="w-12 h-12 rounded-full bg-blue-50 flex items-center justify-center mr-4">
-                          <IconPlus size="large" className="text-blue-500" />
-                        </div>
-                        <div>
-                          <Text className="text-xl font-semibold">{t('在线充值')}</Text>
-                          <div className="text-gray-500 text-sm">{t('支持多种支付方式')}</div>
-                        </div>
-                      </div>
-
-                      <div className="space-y-4">
-                        <div>
-                          <div className="flex justify-between mb-2">
-                            <Text strong>{t('充值数量')}</Text>
-                            {amountLoading ? (
-                              <Skeleton.Title style={{ width: '80px', height: '14px' }} />
-                            ) : (
-                              <Text type="tertiary">{t('实付金额:') + ' ' + renderAmount()}</Text>
-                            )}
-                          </div>
-                          <InputNumber
-                            disabled={!enableOnlineTopUp}
-                            placeholder={
-                              t('充值数量,最低 ') + renderQuotaWithAmount(minTopUp)
-                            }
-                            value={topUpCount}
-                            min={minTopUp}
-                            max={999999999}
-                            step={1}
-                            precision={0}
-                            onChange={async (value) => {
-                              if (value && value >= 1) {
-                                setTopUpCount(value);
-                                await getAmount(value);
-                              }
-                            }}
-                            onBlur={(e) => {
-                              const value = parseInt(e.target.value);
-                              if (!value || value < 1) {
-                                setTopUpCount(1);
-                                getAmount(1);
-                              }
-                            }}
-                            size="large"
-                            className="!rounded-lg w-full"
-                            prefix={<IconCreditCard />}
-                            formatter={(value) => value ? `${value}` : ''}
-                            parser={(value) => value ? parseInt(value.replace(/[^\d]/g, '')) : 0}
-                          />
-                        </div>
-
-                        <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
-                          <Button
-                            type="primary"
-                            theme="solid"
-                            onClick={async () => {
-                              preTopUp('zfb');
-                            }}
-                            size="large"
-                            className="!rounded-lg !bg-blue-500 hover:!bg-blue-600 h-14"
-                            disabled={!enableOnlineTopUp}
-                            loading={paymentLoading}
-                            icon={<SiAlipay size={20} />}
-                          >
-                            <span className="ml-2">{t('支付宝')}</span>
-                          </Button>
-                          <Button
-                            type="primary"
-                            theme="solid"
-                            onClick={async () => {
-                              preTopUp('wx');
-                            }}
-                            size="large"
-                            className="!rounded-lg !bg-green-500 hover:!bg-green-600 h-14"
-                            disabled={!enableOnlineTopUp}
-                            loading={paymentLoading}
-                            icon={<SiWechat size={20} />}
-                          >
-                            <span className="ml-2">{t('微信')}</span>
-                          </Button>
-                        </div>
-
-                        {!enableOnlineTopUp && (
-                          <Banner
-                            fullMode={false}
-                            type="warning"
-                            icon={null}
-                            closeIcon={null}
-                            className="!rounded-lg"
-                            title={
-                              <div style={{ fontWeight: 600, fontSize: '14px', lineHeight: '20px' }}>
-                                {t('在线充值功能未开启')}
-                              </div>
-                            }
-                            description={
-                              <div>
-                                {t('管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。')}
-                              </div>
-                            }
-                          />
-                        )}
-                      </div>
-                    </div>
                   </div>
                 </div>
               </Card>