mirror of
https://github.com/QuantumNous/new-api.git
synced 2026-03-30 02:25:00 +00:00
✨ feat(layout): refine footer visibility logic to target CardPro component pages
- 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.
This commit is contained in:
@@ -6,4 +6,4 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"]
|
"include": ["src/**/*"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,9 @@ const TwoFactorAuthModal = ({
|
|||||||
autoFocus
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<Typography.Text type='tertiary' size='small' className='mt-2 block'>
|
<Typography.Text type='tertiary' size='small' className='mt-2 block'>
|
||||||
{t('支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。')}
|
{t(
|
||||||
|
'支持6位TOTP验证码或8位备用码,可到`个人设置-安全设置-两步验证设置`配置或查看。',
|
||||||
|
)}
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -48,9 +48,19 @@ const PageLayout = () => {
|
|||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
|
|
||||||
const shouldHideFooter =
|
const cardProPages = [
|
||||||
location.pathname.startsWith('/console') ||
|
'/console/channel',
|
||||||
location.pathname === '/pricing';
|
'/console/log',
|
||||||
|
'/console/redemption',
|
||||||
|
'/console/user',
|
||||||
|
'/console/token',
|
||||||
|
'/console/midjourney',
|
||||||
|
'/console/task',
|
||||||
|
'/console/models',
|
||||||
|
'/pricing',
|
||||||
|
];
|
||||||
|
|
||||||
|
const shouldHideFooter = cardProPages.includes(location.pathname);
|
||||||
|
|
||||||
const shouldInnerPadding =
|
const shouldInnerPadding =
|
||||||
location.pathname.includes('/console') &&
|
location.pathname.includes('/console') &&
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ import { useTranslation } from 'react-i18next';
|
|||||||
const SystemSetting = () => {
|
const SystemSetting = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
let [inputs, setInputs] = useState({
|
let [inputs, setInputs] = useState({
|
||||||
|
|
||||||
PasswordLoginEnabled: '',
|
PasswordLoginEnabled: '',
|
||||||
PasswordRegisterEnabled: '',
|
PasswordRegisterEnabled: '',
|
||||||
EmailVerificationEnabled: '',
|
EmailVerificationEnabled: '',
|
||||||
@@ -212,7 +211,9 @@ const SystemSetting = () => {
|
|||||||
setInputs(newInputs);
|
setInputs(newInputs);
|
||||||
setOriginInputs(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']);
|
setDomainFilterMode(!!newInputs['fetch_setting.domain_filter_mode']);
|
||||||
}
|
}
|
||||||
if (typeof newInputs['fetch_setting.ip_filter_mode'] !== 'undefined') {
|
if (typeof newInputs['fetch_setting.ip_filter_mode'] !== 'undefined') {
|
||||||
@@ -749,14 +750,17 @@ const SystemSetting = () => {
|
|||||||
noLabel
|
noLabel
|
||||||
extraText={t('SSRF防护开关详细说明')}
|
extraText={t('SSRF防护开关详细说明')}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleCheckboxChange('fetch_setting.enable_ssrf_protection', e)
|
handleCheckboxChange(
|
||||||
|
'fetch_setting.enable_ssrf_protection',
|
||||||
|
e,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{t('启用SSRF防护(推荐开启以保护服务器安全)')}
|
{t('启用SSRF防护(推荐开启以保护服务器安全)')}
|
||||||
</Form.Checkbox>
|
</Form.Checkbox>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row
|
<Row
|
||||||
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
||||||
style={{ marginTop: 16 }}
|
style={{ marginTop: 16 }}
|
||||||
@@ -767,14 +771,19 @@ const SystemSetting = () => {
|
|||||||
noLabel
|
noLabel
|
||||||
extraText={t('私有IP访问详细说明')}
|
extraText={t('私有IP访问详细说明')}
|
||||||
onChange={(e) =>
|
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等内网地址)',
|
||||||
|
)}
|
||||||
</Form.Checkbox>
|
</Form.Checkbox>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row
|
<Row
|
||||||
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
||||||
style={{ marginTop: 16 }}
|
style={{ marginTop: 16 }}
|
||||||
@@ -785,7 +794,10 @@ const SystemSetting = () => {
|
|||||||
noLabel
|
noLabel
|
||||||
extraText={t('域名IP过滤详细说明')}
|
extraText={t('域名IP过滤详细说明')}
|
||||||
onChange={(e) =>
|
onChange={(e) =>
|
||||||
handleCheckboxChange('fetch_setting.apply_ip_filter_for_domain', e)
|
handleCheckboxChange(
|
||||||
|
'fetch_setting.apply_ip_filter_for_domain',
|
||||||
|
e,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
style={{ marginBottom: 8 }}
|
style={{ marginBottom: 8 }}
|
||||||
>
|
>
|
||||||
@@ -794,17 +806,23 @@ const SystemSetting = () => {
|
|||||||
<Text strong>
|
<Text strong>
|
||||||
{t(domainFilterMode ? '域名白名单' : '域名黑名单')}
|
{t(domainFilterMode ? '域名白名单' : '域名黑名单')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
|
<Text
|
||||||
{t('支持通配符格式,如:example.com, *.api.example.com')}
|
type='secondary'
|
||||||
|
style={{ display: 'block', marginBottom: 8 }}
|
||||||
|
>
|
||||||
|
{t(
|
||||||
|
'支持通配符格式,如:example.com, *.api.example.com',
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
type='button'
|
type='button'
|
||||||
value={domainFilterMode ? 'whitelist' : 'blacklist'}
|
value={domainFilterMode ? 'whitelist' : 'blacklist'}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
const selected = val && val.target ? val.target.value : val;
|
const selected =
|
||||||
|
val && val.target ? val.target.value : val;
|
||||||
const isWhitelist = selected === 'whitelist';
|
const isWhitelist = selected === 'whitelist';
|
||||||
setDomainFilterMode(isWhitelist);
|
setDomainFilterMode(isWhitelist);
|
||||||
setInputs(prev => ({
|
setInputs((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
'fetch_setting.domain_filter_mode': isWhitelist,
|
'fetch_setting.domain_filter_mode': isWhitelist,
|
||||||
}));
|
}));
|
||||||
@@ -819,9 +837,9 @@ const SystemSetting = () => {
|
|||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setDomainList(value);
|
setDomainList(value);
|
||||||
// 触发Form的onChange事件
|
// 触发Form的onChange事件
|
||||||
setInputs(prev => ({
|
setInputs((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
'fetch_setting.domain_list': value
|
'fetch_setting.domain_list': value,
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
placeholder={t('输入域名后回车,如:example.com')}
|
placeholder={t('输入域名后回车,如:example.com')}
|
||||||
@@ -838,17 +856,21 @@ const SystemSetting = () => {
|
|||||||
<Text strong>
|
<Text strong>
|
||||||
{t(ipFilterMode ? 'IP白名单' : 'IP黑名单')}
|
{t(ipFilterMode ? 'IP白名单' : 'IP黑名单')}
|
||||||
</Text>
|
</Text>
|
||||||
<Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
|
<Text
|
||||||
|
type='secondary'
|
||||||
|
style={{ display: 'block', marginBottom: 8 }}
|
||||||
|
>
|
||||||
{t('支持CIDR格式,如:8.8.8.8, 192.168.1.0/24')}
|
{t('支持CIDR格式,如:8.8.8.8, 192.168.1.0/24')}
|
||||||
</Text>
|
</Text>
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
type='button'
|
type='button'
|
||||||
value={ipFilterMode ? 'whitelist' : 'blacklist'}
|
value={ipFilterMode ? 'whitelist' : 'blacklist'}
|
||||||
onChange={(val) => {
|
onChange={(val) => {
|
||||||
const selected = val && val.target ? val.target.value : val;
|
const selected =
|
||||||
|
val && val.target ? val.target.value : val;
|
||||||
const isWhitelist = selected === 'whitelist';
|
const isWhitelist = selected === 'whitelist';
|
||||||
setIpFilterMode(isWhitelist);
|
setIpFilterMode(isWhitelist);
|
||||||
setInputs(prev => ({
|
setInputs((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
'fetch_setting.ip_filter_mode': isWhitelist,
|
'fetch_setting.ip_filter_mode': isWhitelist,
|
||||||
}));
|
}));
|
||||||
@@ -863,9 +885,9 @@ const SystemSetting = () => {
|
|||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setIpList(value);
|
setIpList(value);
|
||||||
// 触发Form的onChange事件
|
// 触发Form的onChange事件
|
||||||
setInputs(prev => ({
|
setInputs((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
'fetch_setting.ip_list': value
|
'fetch_setting.ip_list': value,
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
placeholder={t('输入IP地址后回车,如:8.8.8.8')}
|
placeholder={t('输入IP地址后回车,如:8.8.8.8')}
|
||||||
@@ -880,7 +902,10 @@ const SystemSetting = () => {
|
|||||||
>
|
>
|
||||||
<Col xs={24} sm={24} md={24} lg={24} xl={24}>
|
<Col xs={24} sm={24} md={24} lg={24} xl={24}>
|
||||||
<Text strong>{t('允许的端口')}</Text>
|
<Text strong>{t('允许的端口')}</Text>
|
||||||
<Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
|
<Text
|
||||||
|
type='secondary'
|
||||||
|
style={{ display: 'block', marginBottom: 8 }}
|
||||||
|
>
|
||||||
{t('支持单个端口和端口范围,如:80, 443, 8000-8999')}
|
{t('支持单个端口和端口范围,如:80, 443, 8000-8999')}
|
||||||
</Text>
|
</Text>
|
||||||
<TagInput
|
<TagInput
|
||||||
@@ -888,15 +913,18 @@ const SystemSetting = () => {
|
|||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
setAllowedPorts(value);
|
setAllowedPorts(value);
|
||||||
// 触发Form的onChange事件
|
// 触发Form的onChange事件
|
||||||
setInputs(prev => ({
|
setInputs((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
'fetch_setting.allowed_ports': value
|
'fetch_setting.allowed_ports': value,
|
||||||
}));
|
}));
|
||||||
}}
|
}}
|
||||||
placeholder={t('输入端口后回车,如:80 或 8000-8999')}
|
placeholder={t('输入端口后回车,如:80 或 8000-8999')}
|
||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
/>
|
/>
|
||||||
<Text type="secondary" style={{ display: 'block', marginBottom: 8 }}>
|
<Text
|
||||||
|
type='secondary'
|
||||||
|
style={{ display: 'block', marginBottom: 8 }}
|
||||||
|
>
|
||||||
{t('端口配置详细说明')}
|
{t('端口配置详细说明')}
|
||||||
</Text>
|
</Text>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
@@ -91,22 +91,7 @@ const REGION_EXAMPLE = {
|
|||||||
|
|
||||||
// 支持并且已适配通过接口获取模型列表的渠道类型
|
// 支持并且已适配通过接口获取模型列表的渠道类型
|
||||||
const MODEL_FETCHABLE_TYPES = new Set([
|
const MODEL_FETCHABLE_TYPES = new Set([
|
||||||
1,
|
1, 4, 14, 34, 17, 26, 24, 47, 25, 20, 23, 31, 35, 40, 42, 48,
|
||||||
4,
|
|
||||||
14,
|
|
||||||
34,
|
|
||||||
17,
|
|
||||||
26,
|
|
||||||
24,
|
|
||||||
47,
|
|
||||||
25,
|
|
||||||
20,
|
|
||||||
23,
|
|
||||||
31,
|
|
||||||
35,
|
|
||||||
40,
|
|
||||||
42,
|
|
||||||
48,
|
|
||||||
43,
|
43,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -279,8 +264,8 @@ const EditChannelModal = (props) => {
|
|||||||
const scrollToSection = (sectionKey) => {
|
const scrollToSection = (sectionKey) => {
|
||||||
const sectionElement = formSectionRefs.current[sectionKey];
|
const sectionElement = formSectionRefs.current[sectionKey];
|
||||||
if (sectionElement) {
|
if (sectionElement) {
|
||||||
sectionElement.scrollIntoView({
|
sectionElement.scrollIntoView({
|
||||||
behavior: 'smooth',
|
behavior: 'smooth',
|
||||||
block: 'start',
|
block: 'start',
|
||||||
inline: 'nearest'
|
inline: 'nearest'
|
||||||
});
|
});
|
||||||
@@ -301,7 +286,7 @@ const EditChannelModal = (props) => {
|
|||||||
} else {
|
} else {
|
||||||
newIndex = currentSectionIndex < availableSections.length - 1 ? currentSectionIndex + 1 : 0;
|
newIndex = currentSectionIndex < availableSections.length - 1 ? currentSectionIndex + 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentSectionIndex(newIndex);
|
setCurrentSectionIndex(newIndex);
|
||||||
scrollToSection(availableSections[newIndex]);
|
scrollToSection(availableSections[newIndex]);
|
||||||
};
|
};
|
||||||
@@ -1340,7 +1325,7 @@ const EditChannelModal = (props) => {
|
|||||||
type='tertiary'
|
type='tertiary'
|
||||||
icon={<IconChevronUp />}
|
icon={<IconChevronUp />}
|
||||||
onClick={() => navigateToSection('up')}
|
onClick={() => navigateToSection('up')}
|
||||||
style={{
|
style={{
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
width: '32px',
|
width: '32px',
|
||||||
height: '32px',
|
height: '32px',
|
||||||
@@ -1356,7 +1341,7 @@ const EditChannelModal = (props) => {
|
|||||||
type='tertiary'
|
type='tertiary'
|
||||||
icon={<IconChevronDown />}
|
icon={<IconChevronDown />}
|
||||||
onClick={() => navigateToSection('down')}
|
onClick={() => navigateToSection('down')}
|
||||||
style={{
|
style={{
|
||||||
borderRadius: '50%',
|
borderRadius: '50%',
|
||||||
width: '32px',
|
width: '32px',
|
||||||
height: '32px',
|
height: '32px',
|
||||||
@@ -1398,8 +1383,8 @@ const EditChannelModal = (props) => {
|
|||||||
>
|
>
|
||||||
{() => (
|
{() => (
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
<div
|
<div
|
||||||
className='p-2'
|
className='p-2'
|
||||||
ref={formContainerRef}
|
ref={formContainerRef}
|
||||||
>
|
>
|
||||||
<div ref={el => formSectionRefs.current.basicInfo = el}>
|
<div ref={el => formSectionRefs.current.basicInfo = el}>
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ const MjLogsFilters = ({
|
|||||||
showClear
|
showClear
|
||||||
pure
|
pure
|
||||||
size='small'
|
size='small'
|
||||||
presets={DATE_RANGE_PRESETS.map(preset => ({
|
presets={DATE_RANGE_PRESETS.map((preset) => ({
|
||||||
text: t(preset.text),
|
text: t(preset.text),
|
||||||
start: preset.start(),
|
start: preset.start(),
|
||||||
end: preset.end()
|
end: preset.end(),
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -36,8 +36,9 @@ import {
|
|||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
TASK_ACTION_FIRST_TAIL_GENERATE,
|
TASK_ACTION_FIRST_TAIL_GENERATE,
|
||||||
TASK_ACTION_GENERATE, TASK_ACTION_REFERENCE_GENERATE,
|
TASK_ACTION_GENERATE,
|
||||||
TASK_ACTION_TEXT_GENERATE
|
TASK_ACTION_REFERENCE_GENERATE,
|
||||||
|
TASK_ACTION_TEXT_GENERATE,
|
||||||
} from '../../../constants/common.constant';
|
} from '../../../constants/common.constant';
|
||||||
import { CHANNEL_OPTIONS } from '../../../constants/channel.constants';
|
import { CHANNEL_OPTIONS } from '../../../constants/channel.constants';
|
||||||
|
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ const TaskLogsFilters = ({
|
|||||||
showClear
|
showClear
|
||||||
pure
|
pure
|
||||||
size='small'
|
size='small'
|
||||||
presets={DATE_RANGE_PRESETS.map(preset => ({
|
presets={DATE_RANGE_PRESETS.map((preset) => ({
|
||||||
text: t(preset.text),
|
text: t(preset.text),
|
||||||
start: preset.start(),
|
start: preset.start(),
|
||||||
end: preset.end()
|
end: preset.end(),
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -57,10 +57,10 @@ const LogsFilters = ({
|
|||||||
showClear
|
showClear
|
||||||
pure
|
pure
|
||||||
size='small'
|
size='small'
|
||||||
presets={DATE_RANGE_PRESETS.map(preset => ({
|
presets={DATE_RANGE_PRESETS.map((preset) => ({
|
||||||
text: t(preset.text),
|
text: t(preset.text),
|
||||||
start: preset.start(),
|
start: preset.start(),
|
||||||
end: preset.end()
|
end: preset.end(),
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ import {
|
|||||||
Space,
|
Space,
|
||||||
Row,
|
Row,
|
||||||
Col,
|
Col,
|
||||||
Spin, Tooltip
|
Spin,
|
||||||
|
Tooltip,
|
||||||
} from '@douyinfe/semi-ui';
|
} from '@douyinfe/semi-ui';
|
||||||
import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si';
|
import { SiAlipay, SiWechat, SiStripe } from 'react-icons/si';
|
||||||
import { CreditCard, Coins, Wallet, BarChart2, TrendingUp } from 'lucide-react';
|
import { CreditCard, Coins, Wallet, BarChart2, TrendingUp } from 'lucide-react';
|
||||||
@@ -266,7 +267,8 @@ const RechargeCard = ({
|
|||||||
{payMethods && payMethods.length > 0 ? (
|
{payMethods && payMethods.length > 0 ? (
|
||||||
<Space wrap>
|
<Space wrap>
|
||||||
{payMethods.map((payMethod) => {
|
{payMethods.map((payMethod) => {
|
||||||
const minTopupVal = Number(payMethod.min_topup) || 0;
|
const minTopupVal =
|
||||||
|
Number(payMethod.min_topup) || 0;
|
||||||
const isStripe = payMethod.type === 'stripe';
|
const isStripe = payMethod.type === 'stripe';
|
||||||
const disabled =
|
const disabled =
|
||||||
(!enableOnlineTopUp && !isStripe) ||
|
(!enableOnlineTopUp && !isStripe) ||
|
||||||
@@ -280,7 +282,9 @@ const RechargeCard = ({
|
|||||||
type='tertiary'
|
type='tertiary'
|
||||||
onClick={() => preTopUp(payMethod.type)}
|
onClick={() => preTopUp(payMethod.type)}
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
loading={paymentLoading && payWay === payMethod.type}
|
loading={
|
||||||
|
paymentLoading && payWay === payMethod.type
|
||||||
|
}
|
||||||
icon={
|
icon={
|
||||||
payMethod.type === 'alipay' ? (
|
payMethod.type === 'alipay' ? (
|
||||||
<SiAlipay size={18} color='#1677FF' />
|
<SiAlipay size={18} color='#1677FF' />
|
||||||
@@ -291,7 +295,10 @@ const RechargeCard = ({
|
|||||||
) : (
|
) : (
|
||||||
<CreditCard
|
<CreditCard
|
||||||
size={18}
|
size={18}
|
||||||
color={payMethod.color || 'var(--semi-color-text-2)'}
|
color={
|
||||||
|
payMethod.color ||
|
||||||
|
'var(--semi-color-text-2)'
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -301,12 +308,22 @@ const RechargeCard = ({
|
|||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
return disabled && minTopupVal > Number(topUpCount || 0) ? (
|
return disabled &&
|
||||||
<Tooltip content={t('此支付方式最低充值金额为') + ' ' + minTopupVal} key={payMethod.type}>
|
minTopupVal > Number(topUpCount || 0) ? (
|
||||||
|
<Tooltip
|
||||||
|
content={
|
||||||
|
t('此支付方式最低充值金额为') +
|
||||||
|
' ' +
|
||||||
|
minTopupVal
|
||||||
|
}
|
||||||
|
key={payMethod.type}
|
||||||
|
>
|
||||||
{buttonEl}
|
{buttonEl}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<React.Fragment key={payMethod.type}>{buttonEl}</React.Fragment>
|
<React.Fragment key={payMethod.type}>
|
||||||
|
{buttonEl}
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Space>
|
</Space>
|
||||||
@@ -324,23 +341,27 @@ const RechargeCard = ({
|
|||||||
<Form.Slot label={t('选择充值额度')}>
|
<Form.Slot label={t('选择充值额度')}>
|
||||||
<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2'>
|
<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2'>
|
||||||
{presetAmounts.map((preset, index) => {
|
{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 originalPrice = preset.value * priceRatio;
|
||||||
const discountedPrice = originalPrice * discount;
|
const discountedPrice = originalPrice * discount;
|
||||||
const hasDiscount = discount < 1.0;
|
const hasDiscount = discount < 1.0;
|
||||||
const actualPay = discountedPrice;
|
const actualPay = discountedPrice;
|
||||||
const save = originalPrice - discountedPrice;
|
const save = originalPrice - discountedPrice;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card
|
<Card
|
||||||
key={index}
|
key={index}
|
||||||
style={{
|
style={{
|
||||||
cursor: 'pointer',
|
cursor: 'pointer',
|
||||||
border: selectedPreset === preset.value
|
border:
|
||||||
? '2px solid var(--semi-color-primary)'
|
selectedPreset === preset.value
|
||||||
: '1px solid var(--semi-color-border)',
|
? '2px solid var(--semi-color-primary)'
|
||||||
|
: '1px solid var(--semi-color-border)',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: '100%'
|
width: '100%',
|
||||||
}}
|
}}
|
||||||
bodyStyle={{ padding: '12px' }}
|
bodyStyle={{ padding: '12px' }}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
@@ -352,24 +373,35 @@ const RechargeCard = ({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div style={{ textAlign: 'center' }}>
|
<div style={{ textAlign: 'center' }}>
|
||||||
<Typography.Title heading={6} style={{ margin: '0 0 8px 0' }}>
|
<Typography.Title
|
||||||
|
heading={6}
|
||||||
|
style={{ margin: '0 0 8px 0' }}
|
||||||
|
>
|
||||||
<Coins size={18} />
|
<Coins size={18} />
|
||||||
{formatLargeNumber(preset.value)}
|
{formatLargeNumber(preset.value)}
|
||||||
{hasDiscount && (
|
{hasDiscount && (
|
||||||
<Tag style={{ marginLeft: 4 }} color="green">
|
<Tag style={{ marginLeft: 4 }} color='green'>
|
||||||
{t('折').includes('off') ?
|
{t('折').includes('off')
|
||||||
((1 - parseFloat(discount)) * 100).toFixed(1) :
|
? (
|
||||||
(discount * 10).toFixed(1)}{t('折')}
|
(1 - parseFloat(discount)) *
|
||||||
</Tag>
|
100
|
||||||
|
).toFixed(1)
|
||||||
|
: (discount * 10).toFixed(1)}
|
||||||
|
{t('折')}
|
||||||
|
</Tag>
|
||||||
)}
|
)}
|
||||||
</Typography.Title>
|
</Typography.Title>
|
||||||
<div style={{
|
<div
|
||||||
color: 'var(--semi-color-text-2)',
|
style={{
|
||||||
fontSize: '12px',
|
color: 'var(--semi-color-text-2)',
|
||||||
margin: '4px 0'
|
fontSize: '12px',
|
||||||
}}>
|
margin: '4px 0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
{t('实付')} {actualPay.toFixed(2)},
|
{t('实付')} {actualPay.toFixed(2)},
|
||||||
{hasDiscount ? `${t('节省')} ${save.toFixed(2)}` : `${t('节省')} 0.00`}
|
{hasDiscount
|
||||||
|
? `${t('节省')} ${save.toFixed(2)}`
|
||||||
|
: `${t('节省')} 0.00`}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
|
|||||||
@@ -80,11 +80,11 @@ const TopUp = () => {
|
|||||||
// 预设充值额度选项
|
// 预设充值额度选项
|
||||||
const [presetAmounts, setPresetAmounts] = useState([]);
|
const [presetAmounts, setPresetAmounts] = useState([]);
|
||||||
const [selectedPreset, setSelectedPreset] = useState(null);
|
const [selectedPreset, setSelectedPreset] = useState(null);
|
||||||
|
|
||||||
// 充值配置信息
|
// 充值配置信息
|
||||||
const [topupInfo, setTopupInfo] = useState({
|
const [topupInfo, setTopupInfo] = useState({
|
||||||
amount_options: [],
|
amount_options: [],
|
||||||
discount: {}
|
discount: {},
|
||||||
});
|
});
|
||||||
|
|
||||||
const topUp = async () => {
|
const topUp = async () => {
|
||||||
@@ -262,9 +262,9 @@ const TopUp = () => {
|
|||||||
if (success) {
|
if (success) {
|
||||||
setTopupInfo({
|
setTopupInfo({
|
||||||
amount_options: data.amount_options || [],
|
amount_options: data.amount_options || [],
|
||||||
discount: data.discount || {}
|
discount: data.discount || {},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 处理支付方式
|
// 处理支付方式
|
||||||
let payMethods = data.pay_methods || [];
|
let payMethods = data.pay_methods || [];
|
||||||
try {
|
try {
|
||||||
@@ -280,10 +280,15 @@ const TopUp = () => {
|
|||||||
payMethods = payMethods.map((method) => {
|
payMethods = payMethods.map((method) => {
|
||||||
// 规范化最小充值数
|
// 规范化最小充值数
|
||||||
const normalizedMinTopup = Number(method.min_topup);
|
const normalizedMinTopup = Number(method.min_topup);
|
||||||
method.min_topup = Number.isFinite(normalizedMinTopup) ? normalizedMinTopup : 0;
|
method.min_topup = Number.isFinite(normalizedMinTopup)
|
||||||
|
? normalizedMinTopup
|
||||||
|
: 0;
|
||||||
|
|
||||||
// Stripe 的最小充值从后端字段回填
|
// 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);
|
const stripeMin = Number(data.stripe_min_topup);
|
||||||
if (Number.isFinite(stripeMin)) {
|
if (Number.isFinite(stripeMin)) {
|
||||||
method.min_topup = stripeMin;
|
method.min_topup = stripeMin;
|
||||||
@@ -313,7 +318,11 @@ const TopUp = () => {
|
|||||||
setPayMethods(payMethods);
|
setPayMethods(payMethods);
|
||||||
const enableStripeTopUp = data.enable_stripe_topup || false;
|
const enableStripeTopUp = data.enable_stripe_topup || false;
|
||||||
const enableOnlineTopUp = data.enable_online_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);
|
setEnableOnlineTopUp(enableOnlineTopUp);
|
||||||
setEnableStripeTopUp(enableStripeTopUp);
|
setEnableStripeTopUp(enableStripeTopUp);
|
||||||
setMinTopUp(minTopUpValue);
|
setMinTopUp(minTopUpValue);
|
||||||
@@ -330,12 +339,12 @@ const TopUp = () => {
|
|||||||
console.log('解析支付方式失败:', e);
|
console.log('解析支付方式失败:', e);
|
||||||
setPayMethods([]);
|
setPayMethods([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果有自定义充值数量选项,使用它们替换默认的预设选项
|
// 如果有自定义充值数量选项,使用它们替换默认的预设选项
|
||||||
if (data.amount_options && data.amount_options.length > 0) {
|
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,
|
value: amount,
|
||||||
discount: data.discount[amount] || 1.0
|
discount: data.discount[amount] || 1.0,
|
||||||
}));
|
}));
|
||||||
setPresetAmounts(customPresets);
|
setPresetAmounts(customPresets);
|
||||||
}
|
}
|
||||||
@@ -483,7 +492,7 @@ const TopUp = () => {
|
|||||||
const selectPresetAmount = (preset) => {
|
const selectPresetAmount = (preset) => {
|
||||||
setTopUpCount(preset.value);
|
setTopUpCount(preset.value);
|
||||||
setSelectedPreset(preset.value);
|
setSelectedPreset(preset.value);
|
||||||
|
|
||||||
// 计算实际支付金额,考虑折扣
|
// 计算实际支付金额,考虑折扣
|
||||||
const discount = preset.discount || topupInfo.discount[preset.value] || 1.0;
|
const discount = preset.discount || topupInfo.discount[preset.value] || 1.0;
|
||||||
const discountedAmount = preset.value * priceRatio * discount;
|
const discountedAmount = preset.value * priceRatio * discount;
|
||||||
|
|||||||
@@ -40,9 +40,10 @@ const PaymentConfirmModal = ({
|
|||||||
amountNumber,
|
amountNumber,
|
||||||
discountRate,
|
discountRate,
|
||||||
}) => {
|
}) => {
|
||||||
const hasDiscount = discountRate && discountRate > 0 && discountRate < 1 && amountNumber > 0;
|
const hasDiscount =
|
||||||
const originalAmount = hasDiscount ? (amountNumber / discountRate) : 0;
|
discountRate && discountRate > 0 && discountRate < 1 && amountNumber > 0;
|
||||||
const discountAmount = hasDiscount ? (originalAmount - amountNumber) : 0;
|
const originalAmount = hasDiscount ? amountNumber / discountRate : 0;
|
||||||
|
const discountAmount = hasDiscount ? originalAmount - amountNumber : 0;
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
title={
|
title={
|
||||||
|
|||||||
@@ -24,26 +24,26 @@ export const DATE_RANGE_PRESETS = [
|
|||||||
{
|
{
|
||||||
text: '今天',
|
text: '今天',
|
||||||
start: () => dayjs().startOf('day').toDate(),
|
start: () => dayjs().startOf('day').toDate(),
|
||||||
end: () => dayjs().endOf('day').toDate()
|
end: () => dayjs().endOf('day').toDate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '近 7 天',
|
text: '近 7 天',
|
||||||
start: () => dayjs().subtract(6, 'day').startOf('day').toDate(),
|
start: () => dayjs().subtract(6, 'day').startOf('day').toDate(),
|
||||||
end: () => dayjs().endOf('day').toDate()
|
end: () => dayjs().endOf('day').toDate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '本周',
|
text: '本周',
|
||||||
start: () => dayjs().startOf('week').toDate(),
|
start: () => dayjs().startOf('week').toDate(),
|
||||||
end: () => dayjs().endOf('week').toDate()
|
end: () => dayjs().endOf('week').toDate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '近 30 天',
|
text: '近 30 天',
|
||||||
start: () => dayjs().subtract(29, 'day').startOf('day').toDate(),
|
start: () => dayjs().subtract(29, 'day').startOf('day').toDate(),
|
||||||
end: () => dayjs().endOf('day').toDate()
|
end: () => dayjs().endOf('day').toDate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
text: '本月',
|
text: '本月',
|
||||||
start: () => dayjs().startOf('month').toDate(),
|
start: () => dayjs().startOf('month').toDate(),
|
||||||
end: () => dayjs().endOf('month').toDate()
|
end: () => dayjs().endOf('month').toDate(),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -131,13 +131,11 @@ export const buildApiPayload = (
|
|||||||
seed: 'seed',
|
seed: 'seed',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Object.entries(parameterMappings).forEach(([key, param]) => {
|
Object.entries(parameterMappings).forEach(([key, param]) => {
|
||||||
const enabled = parameterEnabled[key];
|
const enabled = parameterEnabled[key];
|
||||||
const value = inputs[param];
|
const value = inputs[param];
|
||||||
const hasValue = value !== undefined && value !== null;
|
const hasValue = value !== undefined && value !== null;
|
||||||
|
|
||||||
|
|
||||||
if (enabled && hasValue) {
|
if (enabled && hasValue) {
|
||||||
payload[param] = value;
|
payload[param] = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1074,7 +1074,7 @@ export function renderModelPrice(
|
|||||||
(completionTokens / 1000000) * completionRatioPrice * groupRatio +
|
(completionTokens / 1000000) * completionRatioPrice * groupRatio +
|
||||||
(webSearchCallCount / 1000) * webSearchPrice * groupRatio +
|
(webSearchCallCount / 1000) * webSearchPrice * groupRatio +
|
||||||
(fileSearchCallCount / 1000) * fileSearchPrice * groupRatio +
|
(fileSearchCallCount / 1000) * fileSearchPrice * groupRatio +
|
||||||
(imageGenerationCallPrice * groupRatio);
|
imageGenerationCallPrice * groupRatio;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -183,7 +183,10 @@ export const useSidebar = () => {
|
|||||||
sidebarEventTarget.addEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh);
|
sidebarEventTarget.addEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
sidebarEventTarget.removeEventListener(SIDEBAR_REFRESH_EVENT, handleRefresh);
|
sidebarEventTarget.removeEventListener(
|
||||||
|
SIDEBAR_REFRESH_EVENT,
|
||||||
|
handleRefresh,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}, [adminConfig]);
|
}, [adminConfig]);
|
||||||
|
|
||||||
|
|||||||
@@ -130,19 +130,20 @@ export default function GeneralSettings(props) {
|
|||||||
showClear
|
showClear
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
{inputs.QuotaPerUnit !== '500000' && inputs.QuotaPerUnit !== 500000 && (
|
{inputs.QuotaPerUnit !== '500000' &&
|
||||||
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
inputs.QuotaPerUnit !== 500000 && (
|
||||||
<Form.Input
|
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
||||||
field={'QuotaPerUnit'}
|
<Form.Input
|
||||||
label={t('单位美元额度')}
|
field={'QuotaPerUnit'}
|
||||||
initValue={''}
|
label={t('单位美元额度')}
|
||||||
placeholder={t('一单位货币能兑换的额度')}
|
initValue={''}
|
||||||
onChange={handleFieldChange('QuotaPerUnit')}
|
placeholder={t('一单位货币能兑换的额度')}
|
||||||
showClear
|
onChange={handleFieldChange('QuotaPerUnit')}
|
||||||
onClick={() => setShowQuotaWarning(true)}
|
showClear
|
||||||
/>
|
onClick={() => setShowQuotaWarning(true)}
|
||||||
</Col>
|
/>
|
||||||
)}
|
</Col>
|
||||||
|
)}
|
||||||
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
<Col xs={24} sm={12} md={8} lg={8} xl={8}>
|
||||||
<Form.Input
|
<Form.Input
|
||||||
field={'USDExchangeRate'}
|
field={'USDExchangeRate'}
|
||||||
|
|||||||
@@ -128,7 +128,8 @@ export default function SettingsMonitoring(props) {
|
|||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
setInputs({
|
setInputs({
|
||||||
...inputs,
|
...inputs,
|
||||||
'monitor_setting.auto_test_channel_minutes': parseInt(value),
|
'monitor_setting.auto_test_channel_minutes':
|
||||||
|
parseInt(value),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -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)) {
|
if (!verifyJSON(inputs.AmountOptions)) {
|
||||||
showError(t('自定义充值数量选项不是合法的 JSON 数组'));
|
showError(t('自定义充值数量选项不是合法的 JSON 数组'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (originInputs['AmountDiscount'] !== inputs.AmountDiscount && inputs.AmountDiscount.trim() !== '') {
|
if (
|
||||||
|
originInputs['AmountDiscount'] !== inputs.AmountDiscount &&
|
||||||
|
inputs.AmountDiscount.trim() !== ''
|
||||||
|
) {
|
||||||
if (!verifyJSON(inputs.AmountDiscount)) {
|
if (!verifyJSON(inputs.AmountDiscount)) {
|
||||||
showError(t('充值金额折扣配置不是合法的 JSON 对象'));
|
showError(t('充值金额折扣配置不是合法的 JSON 对象'));
|
||||||
return;
|
return;
|
||||||
@@ -163,10 +169,16 @@ export default function SettingsPaymentGateway(props) {
|
|||||||
options.push({ key: 'PayMethods', value: inputs.PayMethods });
|
options.push({ key: 'PayMethods', value: inputs.PayMethods });
|
||||||
}
|
}
|
||||||
if (originInputs['AmountOptions'] !== inputs.AmountOptions) {
|
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) {
|
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 文本')}
|
placeholder={t('为一个 JSON 文本')}
|
||||||
autosize
|
autosize
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Row
|
<Row
|
||||||
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
||||||
style={{ marginTop: 16 }}
|
style={{ marginTop: 16 }}
|
||||||
@@ -282,13 +294,17 @@ export default function SettingsPaymentGateway(props) {
|
|||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
field='AmountOptions'
|
field='AmountOptions'
|
||||||
label={t('自定义充值数量选项')}
|
label={t('自定义充值数量选项')}
|
||||||
placeholder={t('为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]')}
|
placeholder={t(
|
||||||
|
'为一个 JSON 数组,例如:[10, 20, 50, 100, 200, 500]',
|
||||||
|
)}
|
||||||
autosize
|
autosize
|
||||||
extraText={t('设置用户可选择的充值数量选项,例如:[10, 20, 50, 100, 200, 500]')}
|
extraText={t(
|
||||||
|
'设置用户可选择的充值数量选项,例如:[10, 20, 50, 100, 200, 500]',
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Row
|
<Row
|
||||||
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
gutter={{ xs: 8, sm: 16, md: 24, lg: 24, xl: 24, xxl: 24 }}
|
||||||
style={{ marginTop: 16 }}
|
style={{ marginTop: 16 }}
|
||||||
@@ -297,13 +313,17 @@ export default function SettingsPaymentGateway(props) {
|
|||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
field='AmountDiscount'
|
field='AmountDiscount'
|
||||||
label={t('充值金额折扣配置')}
|
label={t('充值金额折扣配置')}
|
||||||
placeholder={t('为一个 JSON 对象,例如:{"100": 0.95, "200": 0.9, "500": 0.85}')}
|
placeholder={t(
|
||||||
|
'为一个 JSON 对象,例如:{"100": 0.95, "200": 0.9, "500": 0.85}',
|
||||||
|
)}
|
||||||
autosize
|
autosize
|
||||||
extraText={t('设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{"100": 0.95, "200": 0.9, "500": 0.85}')}
|
extraText={t(
|
||||||
|
'设置不同充值金额对应的折扣,键为充值金额,值为折扣率,例如:{"100": 0.95, "200": 0.9, "500": 0.85}',
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
|
||||||
<Button onClick={submitPayAddress}>{t('更新支付设置')}</Button>
|
<Button onClick={submitPayAddress}>{t('更新支付设置')}</Button>
|
||||||
</Form.Section>
|
</Form.Section>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@@ -226,8 +226,12 @@ export default function ModelRatioSettings(props) {
|
|||||||
<Col xs={24} sm={16}>
|
<Col xs={24} sm={16}>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label={t('图片输入倍率(仅部分模型支持该计费)')}
|
label={t('图片输入倍率(仅部分模型支持该计费)')}
|
||||||
extraText={t('图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费')}
|
extraText={t(
|
||||||
placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率,例如:{"gpt-image-1": 2}')}
|
'图片输入相关的倍率设置,键为模型名称,值为倍率,仅部分模型支持该计费',
|
||||||
|
)}
|
||||||
|
placeholder={t(
|
||||||
|
'为一个 JSON 文本,键为模型名称,值为倍率,例如:{"gpt-image-1": 2}',
|
||||||
|
)}
|
||||||
field={'ImageRatio'}
|
field={'ImageRatio'}
|
||||||
autosize={{ minRows: 6, maxRows: 12 }}
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
trigger='blur'
|
trigger='blur'
|
||||||
@@ -238,9 +242,7 @@ export default function ModelRatioSettings(props) {
|
|||||||
message: '不是合法的 JSON 字符串',
|
message: '不是合法的 JSON 字符串',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onChange={(value) =>
|
onChange={(value) => setInputs({ ...inputs, ImageRatio: value })}
|
||||||
setInputs({ ...inputs, ImageRatio: value })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@@ -249,7 +251,9 @@ export default function ModelRatioSettings(props) {
|
|||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label={t('音频倍率(仅部分模型支持该计费)')}
|
label={t('音频倍率(仅部分模型支持该计费)')}
|
||||||
extraText={t('音频输入相关的倍率设置,键为模型名称,值为倍率')}
|
extraText={t('音频输入相关的倍率设置,键为模型名称,值为倍率')}
|
||||||
placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率,例如:{"gpt-4o-audio-preview": 16}')}
|
placeholder={t(
|
||||||
|
'为一个 JSON 文本,键为模型名称,值为倍率,例如:{"gpt-4o-audio-preview": 16}',
|
||||||
|
)}
|
||||||
field={'AudioRatio'}
|
field={'AudioRatio'}
|
||||||
autosize={{ minRows: 6, maxRows: 12 }}
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
trigger='blur'
|
trigger='blur'
|
||||||
@@ -260,9 +264,7 @@ export default function ModelRatioSettings(props) {
|
|||||||
message: '不是合法的 JSON 字符串',
|
message: '不是合法的 JSON 字符串',
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
onChange={(value) =>
|
onChange={(value) => setInputs({ ...inputs, AudioRatio: value })}
|
||||||
setInputs({ ...inputs, AudioRatio: value })
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@@ -270,8 +272,12 @@ export default function ModelRatioSettings(props) {
|
|||||||
<Col xs={24} sm={16}>
|
<Col xs={24} sm={16}>
|
||||||
<Form.TextArea
|
<Form.TextArea
|
||||||
label={t('音频补全倍率(仅部分模型支持该计费)')}
|
label={t('音频补全倍率(仅部分模型支持该计费)')}
|
||||||
extraText={t('音频输出补全相关的倍率设置,键为模型名称,值为倍率')}
|
extraText={t(
|
||||||
placeholder={t('为一个 JSON 文本,键为模型名称,值为倍率,例如:{"gpt-4o-realtime": 2}')}
|
'音频输出补全相关的倍率设置,键为模型名称,值为倍率',
|
||||||
|
)}
|
||||||
|
placeholder={t(
|
||||||
|
'为一个 JSON 文本,键为模型名称,值为倍率,例如:{"gpt-4o-realtime": 2}',
|
||||||
|
)}
|
||||||
field={'AudioCompletionRatio'}
|
field={'AudioCompletionRatio'}
|
||||||
autosize={{ minRows: 6, maxRows: 12 }}
|
autosize={{ minRows: 6, maxRows: 12 }}
|
||||||
trigger='blur'
|
trigger='blur'
|
||||||
|
|||||||
Reference in New Issue
Block a user