From 5d3a6caae5460712f9256a5e4942021c5fe7ced6 Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Mon, 9 Jun 2025 22:27:39 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix(theme):=20sync=20theme=20sta?= =?UTF-8?q?te=20between=20global=20context=20and=20local=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace local isDarkMode state with global useTheme hook in TopUp component - Replace local isDarkMode state with global useTheme hook in PersonalSetting component - Remove redundant theme detection useEffect hooks that caused state inconsistency - Update theme condition checks from isDarkMode to theme === 'dark' - Fix issue where components showed dark gradients in light mode due to theme state mismatch - Clean up trailing commas in import statements This ensures all components stay synchronized with the global theme system managed by HeaderBar's theme toggle button. --- .../components/settings/PersonalSetting.js | 55 +- web/src/components/table/LogsTable.js | 13 - web/src/i18n/locales/en.json | 15 +- web/src/pages/TopUp/index.js | 915 +++++++++--------- 4 files changed, 492 insertions(+), 506 deletions(-) diff --git a/web/src/components/settings/PersonalSetting.js b/web/src/components/settings/PersonalSetting.js index 50bca76e..3228d184 100644 --- a/web/src/components/settings/PersonalSetting.js +++ b/web/src/components/settings/PersonalSetting.js @@ -19,6 +19,7 @@ import { } from '../../helpers'; import Turnstile from 'react-turnstile'; import { UserContext } from '../../context/User'; +import { useTheme } from '../../context/Theme'; import { Avatar, Banner, @@ -39,7 +40,7 @@ import { AutoComplete, Checkbox, Tabs, - TabPane, + TabPane } from '@douyinfe/semi-ui'; import { IllustrationNoContent, IllustrationNoContentDark } from '@douyinfe/semi-illustrations'; import { @@ -53,7 +54,7 @@ import { IconKey, IconDelete, IconChevronDown, - IconChevronUp, + IconChevronUp } from '@douyinfe/semi-icons'; import { SiTelegram, SiWechat, SiLinux } from 'react-icons/si'; import { Bell, Shield, Webhook, Globe, Settings, UserPlus, ShieldCheck } from 'lucide-react'; @@ -64,6 +65,7 @@ const PersonalSetting = () => { const [userState, userDispatch] = useContext(UserContext); let navigate = useNavigate(); const { t } = useTranslation(); + const theme = useTheme(); const [inputs, setInputs] = useState({ wechat_verification_code: '', @@ -104,33 +106,6 @@ 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'); @@ -413,7 +388,7 @@ const PersonalSetting = () => { { {isRoot() ? ( {t('超级管理员')} @@ -452,7 +427,7 @@ const PersonalSetting = () => { ) : isAdmin() ? ( {t('管理员')} @@ -460,7 +435,7 @@ const PersonalSetting = () => { ) : ( {t('普通用户')} @@ -468,7 +443,7 @@ const PersonalSetting = () => { )} ID: {userState?.user?.id} @@ -737,7 +712,7 @@ const PersonalSetting = () => { shadows='hover' >
-
+
@@ -1255,11 +1230,11 @@ const PersonalSetting = () => { itemKey='price' >
-
-
-
- -
+
+
+
+ +
diff --git a/web/src/components/table/LogsTable.js b/web/src/components/table/LogsTable.js index c41f19bf..217f8050 100644 --- a/web/src/components/table/LogsTable.js +++ b/web/src/components/table/LogsTable.js @@ -1,18 +1,5 @@ import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { - CreditCard, - ShoppingCart, - Settings, - Server, - AlertTriangle, - HelpCircle, - Zap, - Play, - Clock, - Hash, - Key -} from 'lucide-react'; import { API, copy, diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 2c3c6330..06cfc8f8 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -837,7 +837,18 @@ "支付宝": "Alipay", "待使用收益": "Proceeds to be used", "邀请人数": "Number of people invited", - "兑换余额": "Exchange balance", + "兑换码充值": "Redemption code recharge", + "使用兑换码快速充值": "Use redemption code to quickly recharge", + "支付方式": "Payment method", + "邀请奖励": "Invite reward", + "或输入自定义金额": "Or enter a custom amount", + "选择充值额度": "Select recharge amount", + "实付": "Actual payment", + "快速方便的充值方式": "Quick and convenient recharge method", + "邀请好友获得额外奖励": "Invite friends to get additional rewards", + "邀请好友注册,好友充值后您可获得相应奖励": "Invite friends to register, and you can get the corresponding reward after the friend recharges", + "通过划转功能将奖励额度转入到您的账户余额中": "Transfer the reward amount to your account balance through the transfer function", + "邀请的好友越多,获得的奖励越多": "The more friends you invite, the more rewards you will get", "在线充值": "Online recharge", "充值数量,最低 ": "Recharge quantity, minimum", "请选择充值金额": "Please select the recharge amount", @@ -954,7 +965,7 @@ "任务ID": "Task ID", "周": "week", "总计:": "Total:", - "划转": "transfer", + "划转到余额": "Transfer to balance", "可用额度": "Available credit", "邀请码:": "Invitation code:", "最低": "lowest", diff --git a/web/src/pages/TopUp/index.js b/web/src/pages/TopUp/index.js index 69d940b7..893858f7 100644 --- a/web/src/pages/TopUp/index.js +++ b/web/src/pages/TopUp/index.js @@ -6,12 +6,11 @@ import { showSuccess, renderQuota, renderQuotaWithAmount, - stringToColor, copy, getQuotaPerUnit } from '../../helpers'; import { - Layout, + Avatar, Typography, Card, Button, @@ -21,24 +20,30 @@ import { InputNumber, Banner, Skeleton, + Divider, } from '@douyinfe/semi-ui'; -import { - IconCreditCard, - IconGift, - IconPlus, - IconLink, -} from '@douyinfe/semi-icons'; import { SiAlipay, SiWechat } from 'react-icons/si'; import { useTranslation } from 'react-i18next'; import { UserContext } from '../../context/User'; import { StatusContext } from '../../context/Status/index.js'; +import { useTheme } from '../../context/Theme'; +import { + CreditCard, + Gift, + Link as LinkIcon, + Copy, + Users, + User, + Coins +} from 'lucide-react'; -const { Text } = Typography; +const { Text, Title } = Typography; const TopUp = () => { const { t } = useTranslation(); const [userState, userDispatch] = useContext(UserContext); const [statusState] = useContext(StatusContext); + const theme = useTheme(); const [redemptionCode, setRedemptionCode] = useState(''); const [topUpCode, setTopUpCode] = useState(''); @@ -47,6 +52,7 @@ const TopUp = () => { const [topUpCount, setTopUpCount] = useState(statusState?.status?.min_topup || 1); const [topUpLink, setTopUpLink] = useState(statusState?.status?.top_up_link || ''); const [enableOnlineTopUp, setEnableOnlineTopUp] = useState(statusState?.status?.enable_online_topup || false); + const [priceRatio, setPriceRatio] = useState(statusState?.status?.price || 1); const [userQuota, setUserQuota] = useState(0); const [isSubmitting, setIsSubmitting] = useState(false); const [open, setOpen] = useState(false); @@ -55,13 +61,25 @@ 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(''); const [openTransfer, setOpenTransfer] = useState(false); const [transferAmount, setTransferAmount] = useState(0); + // 预设充值额度选项 + const [presetAmounts, setPresetAmounts] = useState([ + { value: 5 }, + { value: 10 }, + { value: 30 }, + { value: 50 }, + { value: 100 }, + { value: 300 }, + { value: 500 }, + { value: 1000 } + ]); + const [selectedPreset, setSelectedPreset] = useState(null); + const getUsername = () => { if (userState.user) { return userState.user.username; @@ -251,38 +269,11 @@ const TopUp = () => { }; // 复制邀请链接 - const handleAffLinkClick = async (e) => { - e.target.select(); - await copy(e.target.value); + const handleAffLinkClick = async () => { + await copy(affLink); 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); @@ -300,6 +291,7 @@ const TopUp = () => { setTopUpCount(statusState.status.min_topup || 1); setTopUpLink(statusState.status.top_up_link || ''); setEnableOnlineTopUp(statusState.status.enable_online_topup || false); + setPriceRatio(statusState.status.price || 1); } }, [statusState?.status]); @@ -342,432 +334,453 @@ const TopUp = () => { setOpenTransfer(false); }; + // 选择预设充值额度 + const selectPresetAmount = (preset) => { + setTopUpCount(preset.value); + setSelectedPreset(preset.value); + setAmount(preset.value * priceRatio); + }; + + // 格式化大数字显示 + const formatLargeNumber = (num) => { + return num.toString(); + }; + return ( -
- - - {/* 划转模态框 */} - - - {t('请输入要划转的数量')} -
- } - visible={openTransfer} - onOk={transfer} - onCancel={handleTransferCancel} - maskClosable={false} - size={'small'} - centered={true} - > -
-
- - {t('可用额度')} {renderQuota(userState?.user?.aff_quota)} - - -
-
- - {t('划转额度')} {renderQuota(transferAmount)}{' '} - {t('最低') + renderQuota(getQuotaPerUnit())} - - setTransferAmount(value)} - disabled={false} - size="large" - className="!rounded-lg w-full" - /> -
-
- +
+ {/* 划转模态框 */} + + + {t('划转邀请额度')} +
+ } + visible={openTransfer} + onOk={transfer} + onCancel={handleTransferCancel} + maskClosable={false} + size="small" + centered + > +
+
+ + {t('可用邀请额度')} + + +
+
+ + {t('划转额度')} ({t('最低') + renderQuota(getQuotaPerUnit())}) + + setTransferAmount(value)} + size="large" + className="w-full" + /> +
+
+ - - - {t('充值确认')} -
- } - visible={open} - onOk={onlineTopUp} - onCancel={handleCancel} - maskClosable={false} - size={'small'} - centered={true} - confirmLoading={confirmLoading} - > -
-
- {t('充值数量')}: - {topUpCount} -
-
- {t('实付金额')}: - {amountLoading ? ( - - ) : ( - {renderAmount()} - )} -
-
- + {/* 充值确认模态框 */} + + + {t('充值确认')} +
+ } + visible={open} + onOk={onlineTopUp} + onCancel={handleCancel} + maskClosable={false} + size="small" + centered + confirmLoading={confirmLoading} + > +
+
+ {t('充值数量')}: + {renderQuotaWithAmount(topUpCount)} +
+
+ {t('实付金额')}: + {amountLoading ? ( + + ) : ( + {renderAmount()} + )} +
+
+ {t('支付方式')}: + + {payWay === 'zfb' ? ( +
+ + {t('支付宝')} +
+ ) : ( +
+ + {t('微信')} +
+ )} +
+
+
+ -
-
- - -
-
-
-
+
+ {/* 左侧充值区域 */} +
+ {/* 在线充值卡片 */} + +
+
+ + + +
+ + {t('在线充值')} + + + {t('快速方便的充值方式')} + +
-
-
-
- {userDataLoading ? ( - - ) : ( -
- {t('尊敬的')} {getUsername()} -
- )} -
-
- -
-
- -
-
- {t('当前余额')} -
- {userDataLoading ? ( - - ) : ( -
- {renderQuota(userState?.user?.quota || userQuota)} +
+ {userDataLoading ? ( + + ) : ( + +
+ + {getUsername()} ({getUserRole()}) + {getUsername()}
+
+ )} +
+
+
+ } + > +
+ {/* 账户余额信息 */} +
+ + + {t('当前余额')} + + {userDataLoading ? ( + + ) : ( +
+ {renderQuota(userState?.user?.quota || userQuota)} +
+ )} +
+ + + {t('历史消耗')} + + {userDataLoading ? ( + + ) : ( +
+ {renderQuota(userState?.user?.used_quota || 0)} +
+ )} +
+
+ + {enableOnlineTopUp && ( + <> + {/* 预设充值额度卡片网格 */} +
+ {t('选择充值额度')} +
+ {presetAmounts.map((preset, index) => ( + selectPresetAmount(preset)} + className={`cursor-pointer !rounded-2xl transition-all hover:shadow-md ${selectedPreset === preset.value + ? 'border-blue-500' + : 'border-gray-200 hover:border-gray-300' + }`} + bodyStyle={{ textAlign: 'center' }} + > +
+ + {formatLargeNumber(preset.value)} +
+
+ {t('实付')} ¥{(preset.value * priceRatio).toFixed(2)} +
+
+ ))} +
+
+ + + {t('或输入自定义金额')} + + +
+
+ {t('充值数量')} + {amountLoading ? ( + + ) : ( + {t('实付金额:') + renderAmount()} )}
- -
-
-
-
- {t('历史消耗')} -
- {userDataLoading ? ( - - ) : ( -
- {renderQuota(userState?.user?.used_quota || 0)} -
- )} -
-
-
- {t('用户分组')} -
- {userDataLoading ? ( - - ) : ( -
- {userState?.user?.group || t('默认')} -
- )} -
-
-
- {t('用户角色')} -
- {userDataLoading ? ( - - ) : ( -
- {getUserRole()} -
- )} -
-
- -
- {userDataLoading ? ( - - ) : ( -
- ID: {userState?.user?.id || '---'} -
- )} -
-
- -
+ { + if (value && value >= 1) { + setTopUpCount(value); + setSelectedPreset(null); + await getAmount(value); + } + }} + onBlur={(e) => { + const value = parseInt(e.target.value); + if (!value || value < 1) { + setTopUpCount(1); + getAmount(1); + } + }} + size="large" + className="w-full" + formatter={(value) => value ? `${value}` : ''} + parser={(value) => value ? parseInt(value.replace(/[^\d]/g, '')) : 0} + />
- -
-
- {/* 左侧:在线充值和兑换余额 */} -
- {/* 在线充值部分 */} -
-
-
- -
-
- {t('在线充值')} -
{t('支持多种支付方式')}
-
-
- -
-
-
- {t('充值数量')} - {amountLoading ? ( - - ) : ( - {t('实付金额:') + ' ' + renderAmount()} - )} -
- { - 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={} - formatter={(value) => value ? `${value}` : ''} - parser={(value) => value ? parseInt(value.replace(/[^\d]/g, '')) : 0} - /> -
- -
- - -
- - {!enableOnlineTopUp && ( - - {t('在线充值功能未开启')} -
- } - description={ -
- {t('管理员未开启在线充值功能,请联系管理员开启或使用兑换码充值。')} -
- } - /> - )} -
-
- - {/* 兑换余额部分 */} -
-
-
- -
-
- {t('兑换余额')} -
{t('使用兑换码充值余额')}
-
-
- -
-
- {t('兑换码')} - setRedemptionCode(value)} - size="large" - className="!rounded-lg" - prefix={} - /> -
- -
- {topUpLink && ( - - )} - -
-
-
-
- - {/* 右侧:邀请信息部分 */} -
-
-
-
- -
-
-
- {t('邀请信息')} - -
-
{t('管理您的邀请链接和收益')}
-
-
-
- -
-
- -
{t('待使用收益')}
-
- {renderQuota(userState?.user?.aff_quota)} -
-
- -
{t('总收益')}
-
- {renderQuota(userState?.user?.aff_history_quota)} -
-
- -
{t('邀请人数')}
-
- {userState?.user?.aff_count || 0} -
-
-
- -
- {t('邀请链接')} - } - /> -
-
-
+
+ +
+ + )} + + {!enableOnlineTopUp && ( + + )} + + + {t('兑换码充值')} + + + +
+ + {t('使用兑换码快速充值')} +
+ +
+ setRedemptionCode(value)} + size="large" + /> +
+ +
+ {topUpLink && ( + + )} +
-
- - + +
+ + {/* 右侧邀请信息卡片 */} +
+ +
+
+ + + +
+ + {t('邀请奖励')} + + + {t('邀请好友获得额外奖励')} + +
+
+
+
+ } + > +
+
+ +
+ {t('待使用收益')} + +
+
+ {renderQuota(userState?.user?.aff_quota || 0)} +
+
+ +
+ + {t('总收益')} +
+ {renderQuota(userState?.user?.aff_history_quota || 0)} +
+
+ + {t('邀请人数')} +
+ + {userState?.user?.aff_count || 0} +
+
+
+
+ +
+ {t('邀请链接')} +
+ + +
+ +
+ +
+
+
+ + {t('邀请好友注册,好友充值后您可获得相应奖励')} + +
+
+
+ + {t('通过划转功能将奖励额度转入到您的账户余额中')} + +
+
+
+ + {t('邀请的好友越多,获得的奖励越多')} + +
+
+
+
+
+
+ +
+
); };