From f354e5de23cc5ef2df66185d9a99818b56df9375 Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sat, 27 Sep 2025 18:47:53 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20feat(layout):=20refine=20footer?= =?UTF-8?q?=20visibility=20logic=20to=20target=20CardPro=20component=20pag?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace blanket console route footer hiding with specific page targeting - Only hide footer on pages that use CardPro component: * /console/channel (channels management) * /console/log (usage logs) * /console/redemption (redemption codes) * /console/user (user management) * /console/token (token management) * /console/midjourney (midjourney logs) * /console/task (task logs) * /console/models (model management) * /pricing (pricing page) - Footer now displays on other console pages (dashboard, settings, topup, etc.) - Improves UI consistency by showing footer where CardPro's internal pagination isn't used This change ensures footer is only hidden when CardPro component provides its own pagination/footer functionality, while preserving footer visibility on other pages that benefit from the global footer navigation. --- web/jsconfig.json | 2 +- .../common/modals/TwoFactorAuthModal.jsx | 4 +- web/src/components/layout/PageLayout.jsx | 16 +++- web/src/components/settings/SystemSetting.jsx | 74 +++++++++++------ .../channels/modals/EditChannelModal.jsx | 32 +++----- .../table/mj-logs/MjLogsFilters.jsx | 4 +- .../table/task-logs/TaskLogsColumnDefs.jsx | 5 +- .../table/task-logs/TaskLogsFilters.jsx | 4 +- .../table/usage-logs/UsageLogsFilters.jsx | 4 +- web/src/components/topup/RechargeCard.jsx | 82 +++++++++++++------ web/src/components/topup/index.jsx | 31 ++++--- .../topup/modals/PaymentConfirmModal.jsx | 7 +- web/src/constants/console.constants.js | 10 +-- web/src/helpers/api.js | 2 - web/src/helpers/render.jsx | 2 +- web/src/hooks/common/useSidebar.js | 7 +- .../Setting/Operation/SettingsGeneral.jsx | 27 +++--- .../Setting/Operation/SettingsMonitoring.jsx | 3 +- .../Payment/SettingsPaymentGateway.jsx | 42 +++++++--- .../Setting/Ratio/ModelRatioSettings.jsx | 28 ++++--- 20 files changed, 245 insertions(+), 141 deletions(-) diff --git a/web/jsconfig.json b/web/jsconfig.json index ced4d0543..170a7cb4c 100644 --- a/web/jsconfig.json +++ b/web/jsconfig.json @@ -6,4 +6,4 @@ } }, "include": ["src/**/*"] -} \ No newline at end of file +} diff --git a/web/src/components/common/modals/TwoFactorAuthModal.jsx b/web/src/components/common/modals/TwoFactorAuthModal.jsx index 2a9a8b25b..082e63d79 100644 --- a/web/src/components/common/modals/TwoFactorAuthModal.jsx +++ b/web/src/components/common/modals/TwoFactorAuthModal.jsx @@ -135,7 +135,9 @@ const TwoFactorAuthModal = ({ autoFocus /> - {t('支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。')} + {t( + '支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。', + )} diff --git a/web/src/components/layout/PageLayout.jsx b/web/src/components/layout/PageLayout.jsx index f8cdfb0cb..6474501dd 100644 --- a/web/src/components/layout/PageLayout.jsx +++ b/web/src/components/layout/PageLayout.jsx @@ -48,9 +48,19 @@ const PageLayout = () => { const { i18n } = useTranslation(); const location = useLocation(); - const shouldHideFooter = - location.pathname.startsWith('/console') || - location.pathname === '/pricing'; + const cardProPages = [ + '/console/channel', + '/console/log', + '/console/redemption', + '/console/user', + '/console/token', + '/console/midjourney', + '/console/task', + '/console/models', + '/pricing', + ]; + + const shouldHideFooter = cardProPages.includes(location.pathname); const shouldInnerPadding = location.pathname.includes('/console') && diff --git a/web/src/components/settings/SystemSetting.jsx b/web/src/components/settings/SystemSetting.jsx index f9a2c019d..6a567d227 100644 --- a/web/src/components/settings/SystemSetting.jsx +++ b/web/src/components/settings/SystemSetting.jsx @@ -45,7 +45,6 @@ import { useTranslation } from 'react-i18next'; const SystemSetting = () => { const { t } = useTranslation(); let [inputs, setInputs] = useState({ - PasswordLoginEnabled: '', PasswordRegisterEnabled: '', EmailVerificationEnabled: '', @@ -188,7 +187,9 @@ const SystemSetting = () => { setInputs(newInputs); setOriginInputs(newInputs); // 同步模式布尔到本地状态 - if (typeof newInputs['fetch_setting.domain_filter_mode'] !== 'undefined') { + if ( + typeof newInputs['fetch_setting.domain_filter_mode'] !== 'undefined' + ) { setDomainFilterMode(!!newInputs['fetch_setting.domain_filter_mode']); } if (typeof newInputs['fetch_setting.ip_filter_mode'] !== 'undefined') { @@ -695,14 +696,17 @@ const SystemSetting = () => { noLabel extraText={t('SSRF防护开关详细说明')} onChange={(e) => - handleCheckboxChange('fetch_setting.enable_ssrf_protection', e) + handleCheckboxChange( + 'fetch_setting.enable_ssrf_protection', + e, + ) } > {t('启用SSRF防护(推荐开启以保护服务器安全)')} - + { noLabel extraText={t('私有IP访问详细说明')} onChange={(e) => - handleCheckboxChange('fetch_setting.allow_private_ip', e) + handleCheckboxChange( + 'fetch_setting.allow_private_ip', + e, + ) } > - {t('允许访问私有IP地址(127.0.0.1、192.168.x.x等内网地址)')} + {t( + '允许访问私有IP地址(127.0.0.1、192.168.x.x等内网地址)', + )} - + { noLabel extraText={t('域名IP过滤详细说明')} onChange={(e) => - handleCheckboxChange('fetch_setting.apply_ip_filter_for_domain', e) + handleCheckboxChange( + 'fetch_setting.apply_ip_filter_for_domain', + e, + ) } style={{ marginBottom: 8 }} > @@ -740,17 +752,23 @@ const SystemSetting = () => { {t(domainFilterMode ? '域名白名单' : '域名黑名单')} - - {t('支持通配符格式,如:example.com, *.api.example.com')} + + {t( + '支持通配符格式,如:example.com, *.api.example.com', + )} { - const selected = val && val.target ? val.target.value : val; + const selected = + val && val.target ? val.target.value : val; const isWhitelist = selected === 'whitelist'; setDomainFilterMode(isWhitelist); - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, 'fetch_setting.domain_filter_mode': isWhitelist, })); @@ -765,9 +783,9 @@ const SystemSetting = () => { onChange={(value) => { setDomainList(value); // 触发Form的onChange事件 - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, - 'fetch_setting.domain_list': value + 'fetch_setting.domain_list': value, })); }} placeholder={t('输入域名后回车,如:example.com')} @@ -784,17 +802,21 @@ const SystemSetting = () => { {t(ipFilterMode ? 'IP白名单' : 'IP黑名单')} - + {t('支持CIDR格式,如:8.8.8.8, 192.168.1.0/24')} { - const selected = val && val.target ? val.target.value : val; + const selected = + val && val.target ? val.target.value : val; const isWhitelist = selected === 'whitelist'; setIpFilterMode(isWhitelist); - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, 'fetch_setting.ip_filter_mode': isWhitelist, })); @@ -809,9 +831,9 @@ const SystemSetting = () => { onChange={(value) => { setIpList(value); // 触发Form的onChange事件 - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, - 'fetch_setting.ip_list': value + 'fetch_setting.ip_list': value, })); }} placeholder={t('输入IP地址后回车,如:8.8.8.8')} @@ -826,7 +848,10 @@ const SystemSetting = () => { > {t('允许的端口')} - + {t('支持单个端口和端口范围,如:80, 443, 8000-8999')} { onChange={(value) => { setAllowedPorts(value); // 触发Form的onChange事件 - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, - 'fetch_setting.allowed_ports': value + 'fetch_setting.allowed_ports': value, })); }} placeholder={t('输入端口后回车,如:80 或 8000-8999')} style={{ width: '100%' }} /> - + {t('端口配置详细说明')} diff --git a/web/src/components/table/channels/modals/EditChannelModal.jsx b/web/src/components/table/channels/modals/EditChannelModal.jsx index 967bf88a2..6faad4543 100644 --- a/web/src/components/table/channels/modals/EditChannelModal.jsx +++ b/web/src/components/table/channels/modals/EditChannelModal.jsx @@ -87,22 +87,7 @@ const REGION_EXAMPLE = { // 支持并且已适配通过接口获取模型列表的渠道类型 const MODEL_FETCHABLE_TYPES = new Set([ - 1, - 4, - 14, - 34, - 17, - 26, - 24, - 47, - 25, - 20, - 23, - 31, - 35, - 40, - 42, - 48, + 1, 4, 14, 34, 17, 26, 24, 47, 25, 20, 23, 31, 35, 40, 42, 48, ]); function type2secretPrompt(type) { @@ -822,7 +807,9 @@ const EditChannelModal = (props) => { delete localInputs.key; } } else { - localInputs.key = batch ? JSON.stringify(keys) : JSON.stringify(keys[0]); + localInputs.key = batch + ? JSON.stringify(keys) + : JSON.stringify(keys[0]); } } } @@ -1218,7 +1205,10 @@ const EditChannelModal = (props) => { value={inputs.vertex_key_type || 'json'} onChange={(value) => { // 更新设置中的 vertex_key_type - handleChannelOtherSettingsChange('vertex_key_type', value); + handleChannelOtherSettingsChange( + 'vertex_key_type', + value, + ); // 切换为 api_key 时,关闭批量与手动/文件切换,并清理已选文件 if (value === 'api_key') { setBatch(false); @@ -1238,7 +1228,8 @@ const EditChannelModal = (props) => { /> )} {batch ? ( - inputs.type === 41 && (inputs.vertex_key_type || 'json') === 'json' ? ( + inputs.type === 41 && + (inputs.vertex_key_type || 'json') === 'json' ? ( { ) ) : ( <> - {inputs.type === 41 && (inputs.vertex_key_type || 'json') === 'json' ? ( + {inputs.type === 41 && + (inputs.vertex_key_type || 'json') === 'json' ? ( <> {!batch && (
diff --git a/web/src/components/table/mj-logs/MjLogsFilters.jsx b/web/src/components/table/mj-logs/MjLogsFilters.jsx index 6db96e791..7c61454e0 100644 --- a/web/src/components/table/mj-logs/MjLogsFilters.jsx +++ b/web/src/components/table/mj-logs/MjLogsFilters.jsx @@ -56,10 +56,10 @@ const MjLogsFilters = ({ showClear pure size='small' - presets={DATE_RANGE_PRESETS.map(preset => ({ + presets={DATE_RANGE_PRESETS.map((preset) => ({ text: t(preset.text), start: preset.start(), - end: preset.end() + end: preset.end(), }))} />
diff --git a/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx b/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx index b63c7dd4f..1f097b2b7 100644 --- a/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx +++ b/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx @@ -36,8 +36,9 @@ import { } from 'lucide-react'; import { TASK_ACTION_FIRST_TAIL_GENERATE, - TASK_ACTION_GENERATE, TASK_ACTION_REFERENCE_GENERATE, - TASK_ACTION_TEXT_GENERATE + TASK_ACTION_GENERATE, + TASK_ACTION_REFERENCE_GENERATE, + TASK_ACTION_TEXT_GENERATE, } from '../../../constants/common.constant'; import { CHANNEL_OPTIONS } from '../../../constants/channel.constants'; diff --git a/web/src/components/table/task-logs/TaskLogsFilters.jsx b/web/src/components/table/task-logs/TaskLogsFilters.jsx index e27cea867..3bfae77a4 100644 --- a/web/src/components/table/task-logs/TaskLogsFilters.jsx +++ b/web/src/components/table/task-logs/TaskLogsFilters.jsx @@ -56,10 +56,10 @@ const TaskLogsFilters = ({ showClear pure size='small' - presets={DATE_RANGE_PRESETS.map(preset => ({ + presets={DATE_RANGE_PRESETS.map((preset) => ({ text: t(preset.text), start: preset.start(), - end: preset.end() + end: preset.end(), }))} /> diff --git a/web/src/components/table/usage-logs/UsageLogsFilters.jsx b/web/src/components/table/usage-logs/UsageLogsFilters.jsx index 58e5a4692..840c82eea 100644 --- a/web/src/components/table/usage-logs/UsageLogsFilters.jsx +++ b/web/src/components/table/usage-logs/UsageLogsFilters.jsx @@ -57,10 +57,10 @@ const LogsFilters = ({ showClear pure size='small' - presets={DATE_RANGE_PRESETS.map(preset => ({ + presets={DATE_RANGE_PRESETS.map((preset) => ({ text: t(preset.text), start: preset.start(), - end: preset.end() + end: preset.end(), }))} /> diff --git a/web/src/components/topup/RechargeCard.jsx b/web/src/components/topup/RechargeCard.jsx index 03ea2b31e..0a299ffa2 100644 --- a/web/src/components/topup/RechargeCard.jsx +++ b/web/src/components/topup/RechargeCard.jsx @@ -30,7 +30,8 @@ import { Space, Row, Col, - Spin, Tooltip + Spin, + Tooltip, } from '@douyinfe/semi-ui'; import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si'; import { CreditCard, Coins, Wallet, BarChart2, TrendingUp } from 'lucide-react'; @@ -266,7 +267,8 @@ const RechargeCard = ({ {payMethods && payMethods.length > 0 ? ( {payMethods.map((payMethod) => { - const minTopupVal = Number(payMethod.min_topup) || 0; + const minTopupVal = + Number(payMethod.min_topup) || 0; const isStripe = payMethod.type === 'stripe'; const disabled = (!enableOnlineTopUp && !isStripe) || @@ -280,7 +282,9 @@ const RechargeCard = ({ type='tertiary' onClick={() => preTopUp(payMethod.type)} disabled={disabled} - loading={paymentLoading && payWay === payMethod.type} + loading={ + paymentLoading && payWay === payMethod.type + } icon={ payMethod.type === 'alipay' ? ( @@ -291,7 +295,10 @@ const RechargeCard = ({ ) : ( ) } @@ -301,12 +308,22 @@ const RechargeCard = ({ ); - return disabled && minTopupVal > Number(topUpCount || 0) ? ( - + return disabled && + minTopupVal > Number(topUpCount || 0) ? ( + {buttonEl} ) : ( - {buttonEl} + + {buttonEl} + ); })} @@ -324,23 +341,27 @@ const RechargeCard = ({
{presetAmounts.map((preset, index) => { - const discount = preset.discount || topupInfo?.discount?.[preset.value] || 1.0; + const discount = + preset.discount || + topupInfo?.discount?.[preset.value] || + 1.0; const originalPrice = preset.value * priceRatio; const discountedPrice = originalPrice * discount; const hasDiscount = discount < 1.0; const actualPay = discountedPrice; const save = originalPrice - discountedPrice; - + return ( { @@ -352,24 +373,35 @@ const RechargeCard = ({ }} >
- + {formatLargeNumber(preset.value)} {hasDiscount && ( - - {t('折').includes('off') ? - ((1 - parseFloat(discount)) * 100).toFixed(1) : - (discount * 10).toFixed(1)}{t('折')} - + + {t('折').includes('off') + ? ( + (1 - parseFloat(discount)) * + 100 + ).toFixed(1) + : (discount * 10).toFixed(1)} + {t('折')} + )} -
+
{t('实付')} {actualPay.toFixed(2)}, - {hasDiscount ? `${t('节省')} ${save.toFixed(2)}` : `${t('节省')} 0.00`} + {hasDiscount + ? `${t('节省')} ${save.toFixed(2)}` + : `${t('节省')} 0.00`}
diff --git a/web/src/components/topup/index.jsx b/web/src/components/topup/index.jsx index 929a47e39..558c67050 100644 --- a/web/src/components/topup/index.jsx +++ b/web/src/components/topup/index.jsx @@ -80,11 +80,11 @@ const TopUp = () => { // 预设充值额度选项 const [presetAmounts, setPresetAmounts] = useState([]); const [selectedPreset, setSelectedPreset] = useState(null); - + // 充值配置信息 const [topupInfo, setTopupInfo] = useState({ amount_options: [], - discount: {} + discount: {}, }); const topUp = async () => { @@ -262,9 +262,9 @@ const TopUp = () => { if (success) { setTopupInfo({ amount_options: data.amount_options || [], - discount: data.discount || {} + discount: data.discount || {}, }); - + // 处理支付方式 let payMethods = data.pay_methods || []; try { @@ -280,10 +280,15 @@ const TopUp = () => { payMethods = payMethods.map((method) => { // 规范化最小充值数 const normalizedMinTopup = Number(method.min_topup); - method.min_topup = Number.isFinite(normalizedMinTopup) ? normalizedMinTopup : 0; + method.min_topup = Number.isFinite(normalizedMinTopup) + ? normalizedMinTopup + : 0; // Stripe 的最小充值从后端字段回填 - if (method.type === 'stripe' && (!method.min_topup || method.min_topup <= 0)) { + if ( + method.type === 'stripe' && + (!method.min_topup || method.min_topup <= 0) + ) { const stripeMin = Number(data.stripe_min_topup); if (Number.isFinite(stripeMin)) { method.min_topup = stripeMin; @@ -313,7 +318,11 @@ const TopUp = () => { setPayMethods(payMethods); const enableStripeTopUp = data.enable_stripe_topup || false; const enableOnlineTopUp = data.enable_online_topup || false; - const minTopUpValue = enableOnlineTopUp? data.min_topup : enableStripeTopUp? data.stripe_min_topup : 1; + const minTopUpValue = enableOnlineTopUp + ? data.min_topup + : enableStripeTopUp + ? data.stripe_min_topup + : 1; setEnableOnlineTopUp(enableOnlineTopUp); setEnableStripeTopUp(enableStripeTopUp); setMinTopUp(minTopUpValue); @@ -330,12 +339,12 @@ const TopUp = () => { console.log('解析支付方式失败:', e); setPayMethods([]); } - + // 如果有自定义充值数量选项,使用它们替换默认的预设选项 if (data.amount_options && data.amount_options.length > 0) { - const customPresets = data.amount_options.map(amount => ({ + const customPresets = data.amount_options.map((amount) => ({ value: amount, - discount: data.discount[amount] || 1.0 + discount: data.discount[amount] || 1.0, })); setPresetAmounts(customPresets); } @@ -483,7 +492,7 @@ const TopUp = () => { const selectPresetAmount = (preset) => { setTopUpCount(preset.value); setSelectedPreset(preset.value); - + // 计算实际支付金额,考虑折扣 const discount = preset.discount || topupInfo.discount[preset.value] || 1.0; const discountedAmount = preset.value * priceRatio * discount; diff --git a/web/src/components/topup/modals/PaymentConfirmModal.jsx b/web/src/components/topup/modals/PaymentConfirmModal.jsx index 1bffbfed1..8bd5455c7 100644 --- a/web/src/components/topup/modals/PaymentConfirmModal.jsx +++ b/web/src/components/topup/modals/PaymentConfirmModal.jsx @@ -40,9 +40,10 @@ const PaymentConfirmModal = ({ amountNumber, discountRate, }) => { - const hasDiscount = discountRate && discountRate > 0 && discountRate < 1 && amountNumber > 0; - const originalAmount = hasDiscount ? (amountNumber / discountRate) : 0; - const discountAmount = hasDiscount ? (originalAmount - amountNumber) : 0; + const hasDiscount = + discountRate && discountRate > 0 && discountRate < 1 && amountNumber > 0; + const originalAmount = hasDiscount ? amountNumber / discountRate : 0; + const discountAmount = hasDiscount ? originalAmount - amountNumber : 0; return ( dayjs().startOf('day').toDate(), - end: () => dayjs().endOf('day').toDate() + end: () => dayjs().endOf('day').toDate(), }, { text: '近 7 天', start: () => dayjs().subtract(6, 'day').startOf('day').toDate(), - end: () => dayjs().endOf('day').toDate() + end: () => dayjs().endOf('day').toDate(), }, { text: '本周', start: () => dayjs().startOf('week').toDate(), - end: () => dayjs().endOf('week').toDate() + end: () => dayjs().endOf('week').toDate(), }, { text: '近 30 天', start: () => dayjs().subtract(29, 'day').startOf('day').toDate(), - end: () => dayjs().endOf('day').toDate() + end: () => dayjs().endOf('day').toDate(), }, { text: '本月', start: () => dayjs().startOf('month').toDate(), - end: () => dayjs().endOf('month').toDate() + end: () => dayjs().endOf('month').toDate(), }, ]; diff --git a/web/src/helpers/api.js b/web/src/helpers/api.js index bc389b2e8..1ccfffaf2 100644 --- a/web/src/helpers/api.js +++ b/web/src/helpers/api.js @@ -131,13 +131,11 @@ export const buildApiPayload = ( seed: 'seed', }; - Object.entries(parameterMappings).forEach(([key, param]) => { const enabled = parameterEnabled[key]; const value = inputs[param]; const hasValue = value !== undefined && value !== null; - if (enabled && hasValue) { payload[param] = value; } diff --git a/web/src/helpers/render.jsx b/web/src/helpers/render.jsx index c19e2849d..6dc54082b 100644 --- a/web/src/helpers/render.jsx +++ b/web/src/helpers/render.jsx @@ -1072,7 +1072,7 @@ export function renderModelPrice( (completionTokens / 1000000) * completionRatioPrice * groupRatio + (webSearchCallCount / 1000) * webSearchPrice * groupRatio + (fileSearchCallCount / 1000) * fileSearchPrice * groupRatio + - (imageGenerationCallPrice * groupRatio); + imageGenerationCallPrice * groupRatio; return ( <> diff --git a/web/src/hooks/common/useSidebar.js b/web/src/hooks/common/useSidebar.js index 13d76fd86..0b8eb3d8f 100644 --- a/web/src/hooks/common/useSidebar.js +++ b/web/src/hooks/common/useSidebar.js @@ -128,7 +128,7 @@ export const useSidebar = () => { // 刷新用户配置的方法(供外部调用) const refreshUserConfig = async () => { - if (Object.keys(adminConfig).length > 0) { + if (Object.keys(adminConfig).length > 0) { await loadUserConfig(); } @@ -155,7 +155,10 @@ export const useSidebar = () => { sidebarEventTarget.addEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh); return () => { - sidebarEventTarget.removeEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh); + sidebarEventTarget.removeEventListener( + SIDEBAR_REFRESH_EVENT, + handleRefresh, + ); }; }, [adminConfig]); diff --git a/web/src/pages/Setting/Operation/SettingsGeneral.jsx b/web/src/pages/Setting/Operation/SettingsGeneral.jsx index 5af750ec3..b8b925dcf 100644 --- a/web/src/pages/Setting/Operation/SettingsGeneral.jsx +++ b/web/src/pages/Setting/Operation/SettingsGeneral.jsx @@ -130,19 +130,20 @@ export default function GeneralSettings(props) { showClear /> - {inputs.QuotaPerUnit !== '500000' && inputs.QuotaPerUnit !== 500000 && ( - - setShowQuotaWarning(true)} - /> - - )} + {inputs.QuotaPerUnit !== '500000' && + inputs.QuotaPerUnit !== 500000 && ( + + setShowQuotaWarning(true)} + /> + + )} setInputs({ ...inputs, - 'monitor_setting.auto_test_channel_minutes': parseInt(value), + 'monitor_setting.auto_test_channel_minutes': + parseInt(value), }) } /> diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx index d681b6a27..a4f1029a1 100644 --- a/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx +++ b/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx @@ -118,14 +118,20 @@ export default function SettingsPaymentGateway(props) { } } - if (originInputs['AmountOptions'] !== inputs.AmountOptions && inputs.AmountOptions.trim() !== '') { + if ( + originInputs['AmountOptions'] !== inputs.AmountOptions && + inputs.AmountOptions.trim() !== '' + ) { if (!verifyJSON(inputs.AmountOptions)) { showError(t('自定义充值数量选项不是合法的 JSON 数组')); return; } } - if (originInputs['AmountDiscount'] !== inputs.AmountDiscount && inputs.AmountDiscount.trim() !== '') { + if ( + originInputs['AmountDiscount'] !== inputs.AmountDiscount && + inputs.AmountDiscount.trim() !== '' + ) { if (!verifyJSON(inputs.AmountDiscount)) { showError(t('充值金额折扣配置不是合法的 JSON 对象')); return; @@ -163,10 +169,16 @@ export default function SettingsPaymentGateway(props) { options.push({ key: 'PayMethods', value: inputs.PayMethods }); } if (originInputs['AmountOptions'] !== inputs.AmountOptions) { - options.push({ key: 'payment_setting.amount_options', value: inputs.AmountOptions }); + options.push({ + key: 'payment_setting.amount_options', + value: inputs.AmountOptions, + }); } if (originInputs['AmountDiscount'] !== inputs.AmountDiscount) { - options.push({ key: 'payment_setting.amount_discount', value: inputs.AmountDiscount }); + options.push({ + key: 'payment_setting.amount_discount', + value: inputs.AmountDiscount, + }); } // 发送请求 @@ -273,7 +285,7 @@ export default function SettingsPaymentGateway(props) { placeholder={t('为一个 JSON 文本')} autosize /> - + - + - + diff --git a/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx b/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx index ed982edcf..b298cc787 100644 --- a/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx +++ b/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx @@ -226,8 +226,12 @@ export default function ModelRatioSettings(props) { - setInputs({ ...inputs, ImageRatio: value }) - } + onChange={(value) => setInputs({ ...inputs, ImageRatio: value })} /> @@ -249,7 +251,9 @@ export default function ModelRatioSettings(props) { - setInputs({ ...inputs, AudioRatio: value }) - } + onChange={(value) => setInputs({ ...inputs, AudioRatio: value })} /> @@ -270,8 +272,12 @@ export default function ModelRatioSettings(props) { Date: Sat, 27 Sep 2025 18:47:53 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E2=9C=A8=20feat(layout):=20refine=20footer?= =?UTF-8?q?=20visibility=20logic=20to=20target=20CardPro=20component=20pag?= =?UTF-8?q?es?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace blanket console route footer hiding with specific page targeting - Only hide footer on pages that use CardPro component: * /console/channel (channels management) * /console/log (usage logs) * /console/redemption (redemption codes) * /console/user (user management) * /console/token (token management) * /console/midjourney (midjourney logs) * /console/task (task logs) * /console/models (model management) * /pricing (pricing page) - Footer now displays on other console pages (dashboard, settings, topup, etc.) - Improves UI consistency by showing footer where CardPro's internal pagination isn't used This change ensures footer is only hidden when CardPro component provides its own pagination/footer functionality, while preserving footer visibility on other pages that benefit from the global footer navigation. --- web/jsconfig.json | 2 +- .../common/modals/TwoFactorAuthModal.jsx | 4 +- web/src/components/layout/PageLayout.jsx | 16 +++- web/src/components/settings/SystemSetting.jsx | 74 +++++++++++------ .../channels/modals/EditChannelModal.jsx | 31 ++----- .../table/mj-logs/MjLogsFilters.jsx | 4 +- .../table/task-logs/TaskLogsColumnDefs.jsx | 5 +- .../table/task-logs/TaskLogsFilters.jsx | 4 +- .../table/usage-logs/UsageLogsFilters.jsx | 4 +- web/src/components/topup/RechargeCard.jsx | 82 +++++++++++++------ web/src/components/topup/index.jsx | 31 ++++--- .../topup/modals/PaymentConfirmModal.jsx | 7 +- web/src/constants/console.constants.js | 10 +-- web/src/helpers/api.js | 2 - web/src/helpers/render.jsx | 2 +- web/src/hooks/common/useSidebar.js | 5 +- .../Setting/Operation/SettingsGeneral.jsx | 27 +++--- .../Setting/Operation/SettingsMonitoring.jsx | 3 +- .../Payment/SettingsPaymentGateway.jsx | 42 +++++++--- .../Setting/Ratio/ModelRatioSettings.jsx | 28 ++++--- 20 files changed, 240 insertions(+), 143 deletions(-) diff --git a/web/jsconfig.json b/web/jsconfig.json index ced4d0543..170a7cb4c 100644 --- a/web/jsconfig.json +++ b/web/jsconfig.json @@ -6,4 +6,4 @@ } }, "include": ["src/**/*"] -} \ No newline at end of file +} diff --git a/web/src/components/common/modals/TwoFactorAuthModal.jsx b/web/src/components/common/modals/TwoFactorAuthModal.jsx index 2a9a8b25b..082e63d79 100644 --- a/web/src/components/common/modals/TwoFactorAuthModal.jsx +++ b/web/src/components/common/modals/TwoFactorAuthModal.jsx @@ -135,7 +135,9 @@ const TwoFactorAuthModal = ({ autoFocus /> - {t('支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。')} + {t( + '支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。', + )}
diff --git a/web/src/components/layout/PageLayout.jsx b/web/src/components/layout/PageLayout.jsx index f8cdfb0cb..6474501dd 100644 --- a/web/src/components/layout/PageLayout.jsx +++ b/web/src/components/layout/PageLayout.jsx @@ -48,9 +48,19 @@ const PageLayout = () => { const { i18n } = useTranslation(); const location = useLocation(); - const shouldHideFooter = - location.pathname.startsWith('/console') || - location.pathname === '/pricing'; + const cardProPages = [ + '/console/channel', + '/console/log', + '/console/redemption', + '/console/user', + '/console/token', + '/console/midjourney', + '/console/task', + '/console/models', + '/pricing', + ]; + + const shouldHideFooter = cardProPages.includes(location.pathname); const shouldInnerPadding = location.pathname.includes('/console') && diff --git a/web/src/components/settings/SystemSetting.jsx b/web/src/components/settings/SystemSetting.jsx index 112d104a6..780e89fb1 100644 --- a/web/src/components/settings/SystemSetting.jsx +++ b/web/src/components/settings/SystemSetting.jsx @@ -46,7 +46,6 @@ import { useTranslation } from 'react-i18next'; const SystemSetting = () => { const { t } = useTranslation(); let [inputs, setInputs] = useState({ - PasswordLoginEnabled: '', PasswordRegisterEnabled: '', EmailVerificationEnabled: '', @@ -212,7 +211,9 @@ const SystemSetting = () => { setInputs(newInputs); setOriginInputs(newInputs); // 同步模式布尔到本地状态 - if (typeof newInputs['fetch_setting.domain_filter_mode'] !== 'undefined') { + if ( + typeof newInputs['fetch_setting.domain_filter_mode'] !== 'undefined' + ) { setDomainFilterMode(!!newInputs['fetch_setting.domain_filter_mode']); } if (typeof newInputs['fetch_setting.ip_filter_mode'] !== 'undefined') { @@ -749,14 +750,17 @@ const SystemSetting = () => { noLabel extraText={t('SSRF防护开关详细说明')} onChange={(e) => - handleCheckboxChange('fetch_setting.enable_ssrf_protection', e) + handleCheckboxChange( + 'fetch_setting.enable_ssrf_protection', + e, + ) } > {t('启用SSRF防护(推荐开启以保护服务器安全)')}
- + { noLabel extraText={t('私有IP访问详细说明')} onChange={(e) => - handleCheckboxChange('fetch_setting.allow_private_ip', e) + handleCheckboxChange( + 'fetch_setting.allow_private_ip', + e, + ) } > - {t('允许访问私有IP地址(127.0.0.1、192.168.x.x等内网地址)')} + {t( + '允许访问私有IP地址(127.0.0.1、192.168.x.x等内网地址)', + )} - + { noLabel extraText={t('域名IP过滤详细说明')} onChange={(e) => - handleCheckboxChange('fetch_setting.apply_ip_filter_for_domain', e) + handleCheckboxChange( + 'fetch_setting.apply_ip_filter_for_domain', + e, + ) } style={{ marginBottom: 8 }} > @@ -794,17 +806,23 @@ const SystemSetting = () => { {t(domainFilterMode ? '域名白名单' : '域名黑名单')} - - {t('支持通配符格式,如:example.com, *.api.example.com')} + + {t( + '支持通配符格式,如:example.com, *.api.example.com', + )} { - const selected = val && val.target ? val.target.value : val; + const selected = + val && val.target ? val.target.value : val; const isWhitelist = selected === 'whitelist'; setDomainFilterMode(isWhitelist); - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, 'fetch_setting.domain_filter_mode': isWhitelist, })); @@ -819,9 +837,9 @@ const SystemSetting = () => { onChange={(value) => { setDomainList(value); // 触发Form的onChange事件 - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, - 'fetch_setting.domain_list': value + 'fetch_setting.domain_list': value, })); }} placeholder={t('输入域名后回车,如:example.com')} @@ -838,17 +856,21 @@ const SystemSetting = () => { {t(ipFilterMode ? 'IP白名单' : 'IP黑名单')} - + {t('支持CIDR格式,如:8.8.8.8, 192.168.1.0/24')} { - const selected = val && val.target ? val.target.value : val; + const selected = + val && val.target ? val.target.value : val; const isWhitelist = selected === 'whitelist'; setIpFilterMode(isWhitelist); - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, 'fetch_setting.ip_filter_mode': isWhitelist, })); @@ -863,9 +885,9 @@ const SystemSetting = () => { onChange={(value) => { setIpList(value); // 触发Form的onChange事件 - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, - 'fetch_setting.ip_list': value + 'fetch_setting.ip_list': value, })); }} placeholder={t('输入IP地址后回车,如:8.8.8.8')} @@ -880,7 +902,10 @@ const SystemSetting = () => { > {t('允许的端口')} - + {t('支持单个端口和端口范围,如:80, 443, 8000-8999')} { onChange={(value) => { setAllowedPorts(value); // 触发Form的onChange事件 - setInputs(prev => ({ + setInputs((prev) => ({ ...prev, - 'fetch_setting.allowed_ports': value + 'fetch_setting.allowed_ports': value, })); }} placeholder={t('输入端口后回车,如:80 或 8000-8999')} style={{ width: '100%' }} /> - + {t('端口配置详细说明')} diff --git a/web/src/components/table/channels/modals/EditChannelModal.jsx b/web/src/components/table/channels/modals/EditChannelModal.jsx index 09cfb0f05..e9a21c20e 100644 --- a/web/src/components/table/channels/modals/EditChannelModal.jsx +++ b/web/src/components/table/channels/modals/EditChannelModal.jsx @@ -91,22 +91,7 @@ const REGION_EXAMPLE = { // 支持并且已适配通过接口获取模型列表的渠道类型 const MODEL_FETCHABLE_TYPES = new Set([ - 1, - 4, - 14, - 34, - 17, - 26, - 24, - 47, - 25, - 20, - 23, - 31, - 35, - 40, - 42, - 48, + 1, 4, 14, 34, 17, 26, 24, 47, 25, 20, 23, 31, 35, 40, 42, 48, 43, ]); @@ -279,8 +264,8 @@ const EditChannelModal = (props) => { const scrollToSection = (sectionKey) => { const sectionElement = formSectionRefs.current[sectionKey]; if (sectionElement) { - sectionElement.scrollIntoView({ - behavior: 'smooth', + sectionElement.scrollIntoView({ + behavior: 'smooth', block: 'start', inline: 'nearest' }); @@ -301,7 +286,7 @@ const EditChannelModal = (props) => { } else { newIndex = currentSectionIndex < availableSections.length - 1 ? currentSectionIndex + 1 : 0; } - + setCurrentSectionIndex(newIndex); scrollToSection(availableSections[newIndex]); }; @@ -1340,7 +1325,7 @@ const EditChannelModal = (props) => { type='tertiary' icon={} onClick={() => navigateToSection('up')} - style={{ + style={{ borderRadius: '50%', width: '32px', height: '32px', @@ -1356,7 +1341,7 @@ const EditChannelModal = (props) => { type='tertiary' icon={} onClick={() => navigateToSection('down')} - style={{ + style={{ borderRadius: '50%', width: '32px', height: '32px', @@ -1398,8 +1383,8 @@ const EditChannelModal = (props) => { > {() => ( -
formSectionRefs.current.basicInfo = el}> diff --git a/web/src/components/table/mj-logs/MjLogsFilters.jsx b/web/src/components/table/mj-logs/MjLogsFilters.jsx index 6db96e791..7c61454e0 100644 --- a/web/src/components/table/mj-logs/MjLogsFilters.jsx +++ b/web/src/components/table/mj-logs/MjLogsFilters.jsx @@ -56,10 +56,10 @@ const MjLogsFilters = ({ showClear pure size='small' - presets={DATE_RANGE_PRESETS.map(preset => ({ + presets={DATE_RANGE_PRESETS.map((preset) => ({ text: t(preset.text), start: preset.start(), - end: preset.end() + end: preset.end(), }))} />
diff --git a/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx b/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx index b63c7dd4f..1f097b2b7 100644 --- a/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx +++ b/web/src/components/table/task-logs/TaskLogsColumnDefs.jsx @@ -36,8 +36,9 @@ import { } from 'lucide-react'; import { TASK_ACTION_FIRST_TAIL_GENERATE, - TASK_ACTION_GENERATE, TASK_ACTION_REFERENCE_GENERATE, - TASK_ACTION_TEXT_GENERATE + TASK_ACTION_GENERATE, + TASK_ACTION_REFERENCE_GENERATE, + TASK_ACTION_TEXT_GENERATE, } from '../../../constants/common.constant'; import { CHANNEL_OPTIONS } from '../../../constants/channel.constants'; diff --git a/web/src/components/table/task-logs/TaskLogsFilters.jsx b/web/src/components/table/task-logs/TaskLogsFilters.jsx index e27cea867..3bfae77a4 100644 --- a/web/src/components/table/task-logs/TaskLogsFilters.jsx +++ b/web/src/components/table/task-logs/TaskLogsFilters.jsx @@ -56,10 +56,10 @@ const TaskLogsFilters = ({ showClear pure size='small' - presets={DATE_RANGE_PRESETS.map(preset => ({ + presets={DATE_RANGE_PRESETS.map((preset) => ({ text: t(preset.text), start: preset.start(), - end: preset.end() + end: preset.end(), }))} />
diff --git a/web/src/components/table/usage-logs/UsageLogsFilters.jsx b/web/src/components/table/usage-logs/UsageLogsFilters.jsx index 58e5a4692..840c82eea 100644 --- a/web/src/components/table/usage-logs/UsageLogsFilters.jsx +++ b/web/src/components/table/usage-logs/UsageLogsFilters.jsx @@ -57,10 +57,10 @@ const LogsFilters = ({ showClear pure size='small' - presets={DATE_RANGE_PRESETS.map(preset => ({ + presets={DATE_RANGE_PRESETS.map((preset) => ({ text: t(preset.text), start: preset.start(), - end: preset.end() + end: preset.end(), }))} /> diff --git a/web/src/components/topup/RechargeCard.jsx b/web/src/components/topup/RechargeCard.jsx index 03ea2b31e..0a299ffa2 100644 --- a/web/src/components/topup/RechargeCard.jsx +++ b/web/src/components/topup/RechargeCard.jsx @@ -30,7 +30,8 @@ import { Space, Row, Col, - Spin, Tooltip + Spin, + Tooltip, } from '@douyinfe/semi-ui'; import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si'; import { CreditCard, Coins, Wallet, BarChart2, TrendingUp } from 'lucide-react'; @@ -266,7 +267,8 @@ const RechargeCard = ({ {payMethods && payMethods.length > 0 ? ( {payMethods.map((payMethod) => { - const minTopupVal = Number(payMethod.min_topup) || 0; + const minTopupVal = + Number(payMethod.min_topup) || 0; const isStripe = payMethod.type === 'stripe'; const disabled = (!enableOnlineTopUp && !isStripe) || @@ -280,7 +282,9 @@ const RechargeCard = ({ type='tertiary' onClick={() => preTopUp(payMethod.type)} disabled={disabled} - loading={paymentLoading && payWay === payMethod.type} + loading={ + paymentLoading && payWay === payMethod.type + } icon={ payMethod.type === 'alipay' ? ( @@ -291,7 +295,10 @@ const RechargeCard = ({ ) : ( ) } @@ -301,12 +308,22 @@ const RechargeCard = ({ ); - return disabled && minTopupVal > Number(topUpCount || 0) ? ( - + return disabled && + minTopupVal > Number(topUpCount || 0) ? ( + {buttonEl} ) : ( - {buttonEl} + + {buttonEl} + ); })} @@ -324,23 +341,27 @@ const RechargeCard = ({
{presetAmounts.map((preset, index) => { - const discount = preset.discount || topupInfo?.discount?.[preset.value] || 1.0; + const discount = + preset.discount || + topupInfo?.discount?.[preset.value] || + 1.0; const originalPrice = preset.value * priceRatio; const discountedPrice = originalPrice * discount; const hasDiscount = discount < 1.0; const actualPay = discountedPrice; const save = originalPrice - discountedPrice; - + return ( { @@ -352,24 +373,35 @@ const RechargeCard = ({ }} >
- + {formatLargeNumber(preset.value)} {hasDiscount && ( - - {t('折').includes('off') ? - ((1 - parseFloat(discount)) * 100).toFixed(1) : - (discount * 10).toFixed(1)}{t('折')} - + + {t('折').includes('off') + ? ( + (1 - parseFloat(discount)) * + 100 + ).toFixed(1) + : (discount * 10).toFixed(1)} + {t('折')} + )} -
+
{t('实付')} {actualPay.toFixed(2)}, - {hasDiscount ? `${t('节省')} ${save.toFixed(2)}` : `${t('节省')} 0.00`} + {hasDiscount + ? `${t('节省')} ${save.toFixed(2)}` + : `${t('节省')} 0.00`}
diff --git a/web/src/components/topup/index.jsx b/web/src/components/topup/index.jsx index 929a47e39..558c67050 100644 --- a/web/src/components/topup/index.jsx +++ b/web/src/components/topup/index.jsx @@ -80,11 +80,11 @@ const TopUp = () => { // 预设充值额度选项 const [presetAmounts, setPresetAmounts] = useState([]); const [selectedPreset, setSelectedPreset] = useState(null); - + // 充值配置信息 const [topupInfo, setTopupInfo] = useState({ amount_options: [], - discount: {} + discount: {}, }); const topUp = async () => { @@ -262,9 +262,9 @@ const TopUp = () => { if (success) { setTopupInfo({ amount_options: data.amount_options || [], - discount: data.discount || {} + discount: data.discount || {}, }); - + // 处理支付方式 let payMethods = data.pay_methods || []; try { @@ -280,10 +280,15 @@ const TopUp = () => { payMethods = payMethods.map((method) => { // 规范化最小充值数 const normalizedMinTopup = Number(method.min_topup); - method.min_topup = Number.isFinite(normalizedMinTopup) ? normalizedMinTopup : 0; + method.min_topup = Number.isFinite(normalizedMinTopup) + ? normalizedMinTopup + : 0; // Stripe 的最小充值从后端字段回填 - if (method.type === 'stripe' && (!method.min_topup || method.min_topup <= 0)) { + if ( + method.type === 'stripe' && + (!method.min_topup || method.min_topup <= 0) + ) { const stripeMin = Number(data.stripe_min_topup); if (Number.isFinite(stripeMin)) { method.min_topup = stripeMin; @@ -313,7 +318,11 @@ const TopUp = () => { setPayMethods(payMethods); const enableStripeTopUp = data.enable_stripe_topup || false; const enableOnlineTopUp = data.enable_online_topup || false; - const minTopUpValue = enableOnlineTopUp? data.min_topup : enableStripeTopUp? data.stripe_min_topup : 1; + const minTopUpValue = enableOnlineTopUp + ? data.min_topup + : enableStripeTopUp + ? data.stripe_min_topup + : 1; setEnableOnlineTopUp(enableOnlineTopUp); setEnableStripeTopUp(enableStripeTopUp); setMinTopUp(minTopUpValue); @@ -330,12 +339,12 @@ const TopUp = () => { console.log('解析支付方式失败:', e); setPayMethods([]); } - + // 如果有自定义充值数量选项,使用它们替换默认的预设选项 if (data.amount_options && data.amount_options.length > 0) { - const customPresets = data.amount_options.map(amount => ({ + const customPresets = data.amount_options.map((amount) => ({ value: amount, - discount: data.discount[amount] || 1.0 + discount: data.discount[amount] || 1.0, })); setPresetAmounts(customPresets); } @@ -483,7 +492,7 @@ const TopUp = () => { const selectPresetAmount = (preset) => { setTopUpCount(preset.value); setSelectedPreset(preset.value); - + // 计算实际支付金额,考虑折扣 const discount = preset.discount || topupInfo.discount[preset.value] || 1.0; const discountedAmount = preset.value * priceRatio * discount; diff --git a/web/src/components/topup/modals/PaymentConfirmModal.jsx b/web/src/components/topup/modals/PaymentConfirmModal.jsx index 1bffbfed1..8bd5455c7 100644 --- a/web/src/components/topup/modals/PaymentConfirmModal.jsx +++ b/web/src/components/topup/modals/PaymentConfirmModal.jsx @@ -40,9 +40,10 @@ const PaymentConfirmModal = ({ amountNumber, discountRate, }) => { - const hasDiscount = discountRate && discountRate > 0 && discountRate < 1 && amountNumber > 0; - const originalAmount = hasDiscount ? (amountNumber / discountRate) : 0; - const discountAmount = hasDiscount ? (originalAmount - amountNumber) : 0; + const hasDiscount = + discountRate && discountRate > 0 && discountRate < 1 && amountNumber > 0; + const originalAmount = hasDiscount ? amountNumber / discountRate : 0; + const discountAmount = hasDiscount ? originalAmount - amountNumber : 0; return ( dayjs().startOf('day').toDate(), - end: () => dayjs().endOf('day').toDate() + end: () => dayjs().endOf('day').toDate(), }, { text: '近 7 天', start: () => dayjs().subtract(6, 'day').startOf('day').toDate(), - end: () => dayjs().endOf('day').toDate() + end: () => dayjs().endOf('day').toDate(), }, { text: '本周', start: () => dayjs().startOf('week').toDate(), - end: () => dayjs().endOf('week').toDate() + end: () => dayjs().endOf('week').toDate(), }, { text: '近 30 天', start: () => dayjs().subtract(29, 'day').startOf('day').toDate(), - end: () => dayjs().endOf('day').toDate() + end: () => dayjs().endOf('day').toDate(), }, { text: '本月', start: () => dayjs().startOf('month').toDate(), - end: () => dayjs().endOf('month').toDate() + end: () => dayjs().endOf('month').toDate(), }, ]; diff --git a/web/src/helpers/api.js b/web/src/helpers/api.js index bc389b2e8..1ccfffaf2 100644 --- a/web/src/helpers/api.js +++ b/web/src/helpers/api.js @@ -131,13 +131,11 @@ export const buildApiPayload = ( seed: 'seed', }; - Object.entries(parameterMappings).forEach(([key, param]) => { const enabled = parameterEnabled[key]; const value = inputs[param]; const hasValue = value !== undefined && value !== null; - if (enabled && hasValue) { payload[param] = value; } diff --git a/web/src/helpers/render.jsx b/web/src/helpers/render.jsx index 25afacec0..78ff8a44d 100644 --- a/web/src/helpers/render.jsx +++ b/web/src/helpers/render.jsx @@ -1074,7 +1074,7 @@ export function renderModelPrice( (completionTokens / 1000000) * completionRatioPrice * groupRatio + (webSearchCallCount / 1000) * webSearchPrice * groupRatio + (fileSearchCallCount / 1000) * fileSearchPrice * groupRatio + - (imageGenerationCallPrice * groupRatio); + imageGenerationCallPrice * groupRatio; return ( <> diff --git a/web/src/hooks/common/useSidebar.js b/web/src/hooks/common/useSidebar.js index 0ccc58354..76d74ac34 100644 --- a/web/src/hooks/common/useSidebar.js +++ b/web/src/hooks/common/useSidebar.js @@ -183,7 +183,10 @@ export const useSidebar = () => { sidebarEventTarget.addEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh); return () => { - sidebarEventTarget.removeEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh); + sidebarEventTarget.removeEventListener( + SIDEBAR_REFRESH_EVENT, + handleRefresh, + ); }; }, [adminConfig]); diff --git a/web/src/pages/Setting/Operation/SettingsGeneral.jsx b/web/src/pages/Setting/Operation/SettingsGeneral.jsx index 5af750ec3..b8b925dcf 100644 --- a/web/src/pages/Setting/Operation/SettingsGeneral.jsx +++ b/web/src/pages/Setting/Operation/SettingsGeneral.jsx @@ -130,19 +130,20 @@ export default function GeneralSettings(props) { showClear /> - {inputs.QuotaPerUnit !== '500000' && inputs.QuotaPerUnit !== 500000 && ( - - setShowQuotaWarning(true)} - /> - - )} + {inputs.QuotaPerUnit !== '500000' && + inputs.QuotaPerUnit !== 500000 && ( + + setShowQuotaWarning(true)} + /> + + )} setInputs({ ...inputs, - 'monitor_setting.auto_test_channel_minutes': parseInt(value), + 'monitor_setting.auto_test_channel_minutes': + parseInt(value), }) } /> diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx b/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx index d681b6a27..a4f1029a1 100644 --- a/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx +++ b/web/src/pages/Setting/Payment/SettingsPaymentGateway.jsx @@ -118,14 +118,20 @@ export default function SettingsPaymentGateway(props) { } } - if (originInputs['AmountOptions'] !== inputs.AmountOptions && inputs.AmountOptions.trim() !== '') { + if ( + originInputs['AmountOptions'] !== inputs.AmountOptions && + inputs.AmountOptions.trim() !== '' + ) { if (!verifyJSON(inputs.AmountOptions)) { showError(t('自定义充值数量选项不是合法的 JSON 数组')); return; } } - if (originInputs['AmountDiscount'] !== inputs.AmountDiscount && inputs.AmountDiscount.trim() !== '') { + if ( + originInputs['AmountDiscount'] !== inputs.AmountDiscount && + inputs.AmountDiscount.trim() !== '' + ) { if (!verifyJSON(inputs.AmountDiscount)) { showError(t('充值金额折扣配置不是合法的 JSON 对象')); return; @@ -163,10 +169,16 @@ export default function SettingsPaymentGateway(props) { options.push({ key: 'PayMethods', value: inputs.PayMethods }); } if (originInputs['AmountOptions'] !== inputs.AmountOptions) { - options.push({ key: 'payment_setting.amount_options', value: inputs.AmountOptions }); + options.push({ + key: 'payment_setting.amount_options', + value: inputs.AmountOptions, + }); } if (originInputs['AmountDiscount'] !== inputs.AmountDiscount) { - options.push({ key: 'payment_setting.amount_discount', value: inputs.AmountDiscount }); + options.push({ + key: 'payment_setting.amount_discount', + value: inputs.AmountDiscount, + }); } // 发送请求 @@ -273,7 +285,7 @@ export default function SettingsPaymentGateway(props) { placeholder={t('为一个 JSON 文本')} autosize /> - + - + - + diff --git a/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx b/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx index ed982edcf..b298cc787 100644 --- a/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx +++ b/web/src/pages/Setting/Ratio/ModelRatioSettings.jsx @@ -226,8 +226,12 @@ export default function ModelRatioSettings(props) { - setInputs({ ...inputs, ImageRatio: value }) - } + onChange={(value) => setInputs({ ...inputs, ImageRatio: value })} /> @@ -249,7 +251,9 @@ export default function ModelRatioSettings(props) { - setInputs({ ...inputs, AudioRatio: value }) - } + onChange={(value) => setInputs({ ...inputs, AudioRatio: value })} /> @@ -270,8 +272,12 @@ export default function ModelRatioSettings(props) { Date: Thu, 2 Oct 2025 19:00:07 +0800 Subject: [PATCH 3/3] refactor(footer): update footer links and localization text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Removed the 'chatnio' link from the footer. - Added new links for 'CoAI' and 'GPT-Load' in the footer. - Updated the localization key for '基于New API的项目' to '友情链接' for better clarity. - Adjusted the design of the footer to improve layout and visibility of the developer credit. --- web/src/components/layout/Footer.jsx | 57 +++++++++++++++++----------- web/src/i18n/locales/en.json | 3 +- 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/web/src/components/layout/Footer.jsx b/web/src/components/layout/Footer.jsx index 5c210fca8..c827a581b 100644 --- a/web/src/components/layout/Footer.jsx +++ b/web/src/components/layout/Footer.jsx @@ -142,14 +142,6 @@ const FooterBar = () => { > Midjourney-Proxy - - chatnio - {
@@ -200,15 +207,6 @@ const FooterBar = () => { > New API - & - - One API -
@@ -223,10 +221,23 @@ const FooterBar = () => { return (
{footer ? ( -
+
+
+
+ {t('设计与开发由')} + + New API + +
+
) : ( customFooter )} diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 6ffff050c..cb213b997 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -759,7 +759,6 @@ "获取当前设置失败": "Failed to get current settings", "设置已更新": "Settings updated", "更新设置失败": "Update settings failed", - "确认解绑": "Confirm unbinding", "您确定要解绑WxPusher吗?": "Are you sure you want to unbind WxPusher?", "解绑失败": "Unbinding failed", "订阅事件": "Subscribe to events", @@ -1478,7 +1477,7 @@ "相关项目": "Related Projects", "基于New API的项目": "Projects Based on New API", "版权所有": "All rights reserved", - "设计与开发由": "Designed & Developed with love by", + "设计与开发由": "Designed & Developed by", "演示站点": "Demo Site", "页面未找到,请检查您的浏览器地址是否正确": "Page not found, please check if your browser address is correct", "您无权访问此页面,请联系管理员": "You do not have permission to access this page. Please contact the administrator.",