-
-
{monthLabel}
+
+ {monthLabel}
+
@@ -132,9 +146,7 @@ function MonthCalendar({ yearMonth, onMonthChange, dateRender, t }) {
{
}
onPress={() => doCheckin()}
isPending={checkinLoading || !initialLoaded}
isDisabled={!initialLoaded || checkinData.stats?.checked_in_today}
>
+
{!initialLoaded
? t('加载中...')
: checkinData.stats?.checked_in_today
diff --git a/web/src/components/settings/personal/components/TwoFASetting.jsx b/web/src/components/settings/personal/components/TwoFASetting.jsx
index f2694bce7b2..2edb203de87 100644
--- a/web/src/components/settings/personal/components/TwoFASetting.jsx
+++ b/web/src/components/settings/personal/components/TwoFASetting.jsx
@@ -26,12 +26,7 @@ import {
ModalHeader,
useOverlayState,
} from '@heroui/react';
-import {
- AlertTriangle,
- Copy,
- RefreshCw,
- Shield,
-} from 'lucide-react';
+import { AlertTriangle, Copy, RefreshCw, Shield } from 'lucide-react';
import { QRCodeSVG } from 'qrcode.react';
import { API, showError, showSuccess, showWarning } from '../../../../helpers';
@@ -335,9 +330,9 @@ const TwoFASetting = ({ t }) => {
}
onPress={onCopy}
>
+
{t('复制所有代码')}
@@ -364,9 +359,7 @@ const TwoFASetting = ({ t }) => {
{t('未启用')}
)}
{status.locked && (
-
- {t('账户已锁定')}
-
+ {t('账户已锁定')}
)}
@@ -388,27 +381,27 @@ const TwoFASetting = ({ t }) => {
}
onPress={handleSetup2FA}
className='!bg-foreground/85 hover:!bg-foreground'
>
+
{t('启用验证')}
) : (
<>
}
onPress={() => setDisableModalVisible(true)}
className='!bg-foreground/70 hover:!bg-foreground/85'
>
+
{t('禁用两步验证')}
}
onPress={() => setBackupModalVisible(true)}
>
+
{t('重新生成备用码')}
>
@@ -495,8 +488,7 @@ const TwoFASetting = ({ t }) => {
codes={setupData.backup_codes}
title={t('备用恢复代码')}
onCopy={() => {
- const codesText =
- setupData.backup_codes.join('\n');
+ const codesText = setupData.backup_codes.join('\n');
copyTextToClipboard(
codesText,
t('备用码已复制到剪贴板'),
@@ -510,9 +502,7 @@ const TwoFASetting = ({ t }) => {
value={verificationCode}
onValueChange={setVerificationCode}
maxLength={6}
- placeholder={t(
- '输入认证器应用显示的6位数字验证码',
- )}
+ placeholder={t('输入认证器应用显示的6位数字验证码')}
size='lg'
>
@@ -566,11 +556,7 @@ const TwoFASetting = ({ t }) => {
{/* Disable 2FA modal */}
-
+
@@ -605,9 +591,7 @@ const TwoFASetting = ({ t }) => {
-
- {t('永久删除所有备用码(包括未使用的)')}
-
+ {t('永久删除所有备用码(包括未使用的)')}
@@ -669,11 +653,7 @@ const TwoFASetting = ({ t }) => {
{/* Regenerate backup codes modal */}
-
+
diff --git a/web/src/components/table/channels/ChannelsActions.jsx b/web/src/components/table/channels/ChannelsActions.jsx
index 2ebe2b9ff5a..005bb5032cc 100644
--- a/web/src/components/table/channels/ChannelsActions.jsx
+++ b/web/src/components/table/channels/ChannelsActions.jsx
@@ -59,10 +59,10 @@ function ClickDropdown({ label, items }) {
variant='tertiary'
size='sm'
className='w-full md:w-auto'
- endContent={ }
onPress={() => setOpen((prev) => !prev)}
>
{label}
+
{open ? (
ask({
title: t('确定是否要修复数据库一致性?'),
- content: t('进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用'),
+ content: t(
+ '进行该操作时,可能导致渠道访问错误,请仅在数据库出现问题时使用',
+ ),
onConfirm: fixChannelsAbilities,
}),
},
diff --git a/web/src/components/table/channels/modals/CodexUsageModal.jsx b/web/src/components/table/channels/modals/CodexUsageModal.jsx
index ecc552cb50c..e8e0faf99cb 100644
--- a/web/src/components/table/channels/modals/CodexUsageModal.jsx
+++ b/web/src/components/table/channels/modals/CodexUsageModal.jsx
@@ -349,12 +349,8 @@ function CodexUsageView({ t, record, payload, onCopy, onRefresh }) {
- }
- onPress={onRefresh}
- >
+
+
{tt('刷新')}
@@ -510,12 +506,8 @@ function CodexUsageLoader({ t, record, initialPayload, onCopy }) {
{tt('获取用量失败')}
- }
- onPress={fetchUsage}
- >
+
+
{tt('刷新')}
@@ -553,11 +545,7 @@ const CodexUsageModal = ({
return (
-
+
{tt('Codex 帐号与用量')}
@@ -573,11 +561,8 @@ const CodexUsageModal = ({
) : null}
- }
- onPress={onClose}
- >
+
+
{tt('关闭')}
diff --git a/web/src/components/table/channels/modals/EditTagModal.jsx b/web/src/components/table/channels/modals/EditTagModal.jsx
index 7b86299054f..c0c2a789b6d 100644
--- a/web/src/components/table/channels/modals/EditTagModal.jsx
+++ b/web/src/components/table/channels/modals/EditTagModal.jsx
@@ -15,14 +15,7 @@ For commercial licensing, please contact support@quantumnous.com
import React, { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Card, Spinner } from '@heroui/react';
-import {
- Bookmark,
- Code2,
- Save,
- Settings,
- User,
- X,
-} from 'lucide-react';
+import { Bookmark, Code2, Save, Settings, User, X } from 'lucide-react';
import {
API,
showError,
@@ -245,12 +238,8 @@ function MultiSelectChips({
selected ? 'text-primary' : 'text-foreground'
}`}
>
-
- {opt.label ?? String(v)}
-
- {selected ? (
- {'✓'}
- ) : null}
+ {opt.label ?? String(v)}
+ {selected ? {'✓'} : null}
);
@@ -394,10 +383,7 @@ const EditTagModal = ({ visible, tag, handleClose, refresh }) => {
return;
}
const trimmedHeaderOverride = formVals.header_override.trim();
- if (
- trimmedHeaderOverride !== '' &&
- !verifyJSON(trimmedHeaderOverride)
- ) {
+ if (trimmedHeaderOverride !== '' && !verifyJSON(trimmedHeaderOverride)) {
showInfo('请求头覆盖必须是合法的 JSON 格式!');
setLoading(false);
return;
@@ -605,9 +591,7 @@ const EditTagModal = ({ visible, tag, handleClose, refresh }) => {
setField('models', v)}
@@ -819,8 +803,7 @@ const EditTagModal = ({ visible, tag, handleClose, refresh }) => {
- {t('支持变量:')}
- {' '}
+ {t('支持变量:')}{' '}
{t('渠道密钥')}: {'{api_key}'}
@@ -851,9 +834,7 @@ const EditTagModal = ({ visible, tag, handleClose, refresh }) => {
setField('groups', v)}
/>
@@ -867,19 +848,12 @@ const EditTagModal = ({ visible, tag, handleClose, refresh }) => {
diff --git a/web/src/components/table/channels/modals/ModelTestModal.jsx b/web/src/components/table/channels/modals/ModelTestModal.jsx
index ec4ec6ffd57..4a6aede976f 100644
--- a/web/src/components/table/channels/modals/ModelTestModal.jsx
+++ b/web/src/components/table/channels/modals/ModelTestModal.jsx
@@ -215,9 +215,7 @@ const ModelTestModal = ({
return;
}
if (checked) {
- setSelectedModelKeys(
- Array.from(new Set([...selectedModelKeys, key])),
- );
+ setSelectedModelKeys(Array.from(new Set([...selectedModelKeys, key])));
} else {
setSelectedModelKeys(selectedModelKeys.filter((item) => item !== key));
}
@@ -265,12 +263,12 @@ const ModelTestModal = ({
}
onPress={() =>
window.open('/console/setting?tab=ratio', '_blank')
}
className={`w-fit ${warningGhostButtonClass}`}
>
+
{t('前往设置')}
)}
@@ -318,8 +316,7 @@ const ModelTestModal = ({
{currentTestChannel.name} {t('渠道的模型测试')}
- {t('共')}{' '}
- {currentTestChannel.models.split(',').length}{' '}
+ {t('共')} {currentTestChannel.models.split(',').length}{' '}
{t('个模型')}
@@ -344,7 +341,10 @@ const ModelTestModal = ({
className='h-9 w-full min-w-0 rounded-xl border border-border bg-background px-3 text-sm outline-none transition focus:border-primary'
>
{endpointTypeOptions.map((option) => (
-
))}
@@ -381,7 +381,10 @@ const ModelTestModal = ({
{/* Search + actions */}
-
+
{
}
isPending={loading}
- onPress={() =>
- loadKeyStatus(currentPage, pageSize)
- }
+ onPress={() => loadKeyStatus(currentPage, pageSize)}
>
+
{t('刷新')}
{manualDisabledCount + autoDisabledCount > 0 && (
@@ -555,10 +553,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
className='px-4 py-10 text-center'
>
-
+
{t('暂无密钥数据')}
@@ -602,9 +597,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
)}
>
- {timestamp2string(
- record.disabled_time,
- )}
+ {timestamp2string(record.disabled_time)}
)}
@@ -679,9 +672,7 @@ const MultiKeyManageModal = ({ visible, onCancel, channel, onRefresh }) => {
>
) : (
@@ -589,10 +580,10 @@ const OllamaModelModal = ({
color='primary'
isPending={pullLoading}
isDisabled={!pullModelName.trim()}
- startContent={ }
onPress={pullModel}
className='w-full'
>
+
{pullLoading ? t('拉取中...') : t('拉取模型')}
@@ -626,10 +617,8 @@ const OllamaModelModal = ({
placeholder={t('搜索模型...')}
value={searchValue}
onValueChange={setSearchValue}
- startContent={
-
- }
>
+
@@ -654,19 +643,19 @@ const OllamaModelModal = ({
}
isDisabled={selectedModelIds.length === 0}
onPress={handleApplyAllModels}
>
+
{t('加入渠道')}
}
isPending={loading}
onPress={fetchModels}
>
+
{t('刷新')}
@@ -748,11 +737,8 @@ const OllamaModelModal = ({
- }
- onPress={onCancel}
- >
+
+
{t('关闭')}
diff --git a/web/src/components/table/model-deployments/DeploymentsColumnDefs.jsx b/web/src/components/table/model-deployments/DeploymentsColumnDefs.jsx
index 0fa569d3d0b..2de881d15bc 100644
--- a/web/src/components/table/model-deployments/DeploymentsColumnDefs.jsx
+++ b/web/src/components/table/model-deployments/DeploymentsColumnDefs.jsx
@@ -260,7 +260,9 @@ const renderResourceConfig = (resource, t) => {
{memory && (
- {t('内存')}: {memory}
+
+ {t('内存')}: {memory}
+
)}
{gpu && (
@@ -527,8 +529,8 @@ export const getDeploymentsColumns = ({
size='sm'
variant='tertiary'
onPress={() => onViewDetails?.(record)}
- startContent={}
>
+
{t('查看详情')}
@@ -614,11 +616,11 @@ export const getDeploymentsColumns = ({
size='sm'
variant={primaryAction.variant || 'solid'}
color={primaryAction.color}
- startContent={primaryAction.icon}
onPress={primaryAction.onPress}
isDisabled={primaryAction.isDisabled}
className='px-2 text-xs'
>
+ {primaryAction.icon}
{primaryAction.text}
diff --git a/web/src/components/table/model-deployments/modals/CreateDeploymentModal.jsx b/web/src/components/table/model-deployments/modals/CreateDeploymentModal.jsx
index 9e441e14aa0..e399f46fcbe 100644
--- a/web/src/components/table/model-deployments/modals/CreateDeploymentModal.jsx
+++ b/web/src/components/table/model-deployments/modals/CreateDeploymentModal.jsx
@@ -27,14 +27,7 @@ import {
Tooltip,
useOverlayState,
} from '@heroui/react';
-import {
- ChevronDown,
- Copy,
- HelpCircle,
- Minus,
- Plus,
- X,
-} from 'lucide-react';
+import { ChevronDown, Copy, HelpCircle, Minus, Plus, X } from 'lucide-react';
import { API, copy, showError, showSuccess } from '../../../../helpers';
// ----------------------------- helpers -----------------------------
@@ -71,7 +64,8 @@ function StatusChip({ tone = 'grey', size = 'sm', children }) {
blue: 'bg-primary/15 text-primary',
grey: 'bg-surface-secondary text-muted',
};
- const sizeCls = size === 'xs' ? 'px-1.5 py-0.5 text-[10px]' : 'px-2 py-0.5 text-xs';
+ const sizeCls =
+ size === 'xs' ? 'px-1.5 py-0.5 text-[10px]' : 'px-2 py-0.5 text-xs';
return (
{options.length === 0 ? (
-
- {placeholder}
-
+ {placeholder}
) : (
{options.map((option) => {
@@ -248,7 +240,7 @@ function RichMultiSelect({
const opt = options.find((o) => o.value === v);
const label = renderSelectedLabel
? renderSelectedLabel(opt, v)
- : opt?.label ?? String(v);
+ : (opt?.label ?? String(v));
return (
{options.length === 0 ? (
-
- {placeholder}
-
+ {placeholder}
) : (
{options.map((option) => {
@@ -295,9 +285,7 @@ function RichMultiSelect({
- toggle(option.value, option.disabled)
- }
+ onClick={() => toggle(option.value, option.disabled)}
className={`flex w-full items-start gap-2 px-3 py-2 text-left text-sm transition disabled:cursor-not-allowed disabled:opacity-50 ${
selected
? 'bg-primary/10 text-primary'
@@ -1101,10 +1089,7 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
{t('最大GPU数量')}: {hardware.max_gpus}
- 0 ? 'green' : 'red'}
- size='xs'
- >
+ 0 ? 'green' : 'red'} size='xs'>
{t('可用数量')}: {availableCount}
@@ -1162,11 +1147,7 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
return (
-
+
{t('新建容器部署')}
@@ -1211,9 +1192,7 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
type='text'
value={values.resource_private_name || ''}
onChange={(event) =>
- setField('resource_private_name')(
- event.target.value,
- )
+ setField('resource_private_name')(event.target.value)
}
placeholder={t('请输入容器名称')}
className={inputClass}
@@ -1255,7 +1234,8 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
onChange={(event) => {
const v = event.target.value;
setField('image_url')(v);
- if (imageMode === 'custom') customImageRef.current = v;
+ if (imageMode === 'custom')
+ customImageRef.current = v;
}}
placeholder={t('例如:nginx:latest')}
disabled={imageMode === 'builtin'}
@@ -1278,14 +1258,15 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
}
onPress={async () => {
if (!autoOllamaKey) return;
const copied = await copy(autoOllamaKey);
- if (copied) showSuccess(t('已复制自动生成的 API Key'));
+ if (copied)
+ showSuccess(t('已复制自动生成的 API Key'));
else showError(t('复制失败,请手动选择文本复制'));
}}
>
+
{t('复制')}
@@ -1361,9 +1342,7 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
renderSelectedLabel={(option, v) =>
loadingReplicas
? t('部署位置加载中...')
- : option?.label ||
- locationLabelMap[v] ||
- String(v)
+ : option?.label || locationLabelMap[v] || String(v)
}
/>
{errors.location_ids}
@@ -1411,9 +1390,7 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
{t('流量端口')}
-
+
@@ -1457,9 +1434,7 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
-
- {t('镜像仓库用户名')}
-
+ {t('镜像仓库用户名')}
{
}
onPress={() =>
handleAddArrayField('entrypoint')
}
>
+
{t('添加启动命令')}
@@ -1589,9 +1564,9 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
}
onPress={() => handleAddArrayField('args')}
>
+
{t('添加启动参数')}
@@ -1652,10 +1627,7 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
isDisabled={envVariables.length === 1}
aria-label={t('删除')}
onPress={() =>
- handleRemoveEnvVariable(
- index,
- 'env',
- )
+ handleRemoveEnvVariable(index, 'env')
}
>
@@ -1666,11 +1638,9 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
}
- onPress={() =>
- handleAddEnvVariable('env')
- }
+ onPress={() => handleAddEnvVariable('env')}
>
+
{t('添加环境变量')}
@@ -1744,11 +1714,9 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
}
- onPress={() =>
- handleAddEnvVariable('secret')
- }
+ onPress={() => handleAddEnvVariable('secret')}
>
+
{t('添加密钥环境变量')}
@@ -1807,7 +1775,8 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
{t('预估总费用')}
- {typeof priceEstimation.estimated_cost === 'number'
+ {typeof priceEstimation.estimated_cost ===
+ 'number'
? `${priceEstimation.estimated_cost.toFixed(4)} ${currencyLabel}`
: '--'}
@@ -1817,8 +1786,8 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
{t('小时费率')}
- {typeof priceEstimation.price_breakdown?.hourly_rate ===
- 'number'
+ {typeof priceEstimation.price_breakdown
+ ?.hourly_rate === 'number'
? `${priceEstimation.price_breakdown.hourly_rate.toFixed(4)} ${currencyLabel}/h`
: '--'}
@@ -1828,8 +1797,8 @@ const CreateDeploymentModal = ({ visible, onCancel, onSuccess, t }) => {
{t('计算成本')}
- {typeof priceEstimation.price_breakdown?.compute_cost ===
- 'number'
+ {typeof priceEstimation.price_breakdown
+ ?.compute_cost === 'number'
? `${priceEstimation.price_breakdown.compute_cost.toFixed(4)} ${currencyLabel}`
: '--'}
diff --git a/web/src/components/table/model-deployments/modals/EditDeploymentModal.jsx b/web/src/components/table/model-deployments/modals/EditDeploymentModal.jsx
index 2861cb65227..5cde641d6fe 100644
--- a/web/src/components/table/model-deployments/modals/EditDeploymentModal.jsx
+++ b/web/src/components/table/model-deployments/modals/EditDeploymentModal.jsx
@@ -185,16 +185,16 @@ const EditDeploymentModal = ({
variant='tertiary'
onPress={handleClose}
isDisabled={loading}
- startContent={}
>
+
{t('取消')}
}
>
+
{isEdit ? t('更新') : t('创建')}
diff --git a/web/src/components/table/model-deployments/modals/UpdateConfigModal.jsx b/web/src/components/table/model-deployments/modals/UpdateConfigModal.jsx
index 1ac6f1e56d4..8162c1f703c 100644
--- a/web/src/components/table/model-deployments/modals/UpdateConfigModal.jsx
+++ b/web/src/components/table/model-deployments/modals/UpdateConfigModal.jsx
@@ -59,7 +59,13 @@ function StatusChip({ tone = 'grey', children }) {
// Replaces Semi `` / `` with a ``
// element so accordion state is native (no extra hook + accessible by
// default).
-function CollapseSection({ icon, title, suffix, defaultOpen = false, children }) {
+function CollapseSection({
+ icon,
+ title,
+ suffix,
+ defaultOpen = false,
+ children,
+}) {
return (
{children};
}
-const UpdateConfigModal = ({
- visible,
- onCancel,
- deployment,
- onSuccess,
- t,
-}) => {
+const UpdateConfigModal = ({ visible, onCancel, deployment, onSuccess, t }) => {
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState({
image_url: '',
@@ -439,15 +439,18 @@ const UpdateConfigModal = ({
}
onPress={addEnvVar}
>
+
{t('添加')}
{envVars.map((envVar, index) => (
-
+
}
onPress={addSecretEnvVar}
>
+
{t('添加')}
{secretEnvVars.map((envVar, index) => (
-
+
{items.map((item, idx) => (
-
- {item.key}
-
+ {item.key}
{item.value}
))}
@@ -330,8 +328,7 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
{details.total_gpus}
- {t('总计')} {details.total_gpus}{' '}
- {t('个GPU')}
+ {t('总计')} {details.total_gpus} {t('个GPU')}
),
@@ -341,7 +338,8 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
value: (
- {t('每容器GPU数')}: {details.gpus_per_container}
+ {t('每容器GPU数')}:{' '}
+ {details.gpus_per_container}
{t('容器总数')}: {details.total_containers}
@@ -397,14 +395,17 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
},
{
key: t('流量端口'),
- value: details.container_config.traffic_port || 'N/A',
+ value:
+ details.container_config.traffic_port || 'N/A',
},
{
key: t('启动命令'),
value: (
{details.container_config.entrypoint
- ? details.container_config.entrypoint.join(' ')
+ ? details.container_config.entrypoint.join(
+ ' ',
+ )
: 'N/A'}
),
@@ -491,7 +492,6 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
}
onPress={() =>
window.open(
ctr.public_url,
@@ -500,6 +500,7 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
)
}
>
+
{t('访问容器')}
@@ -546,11 +547,7 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
>
{details.locations.map((location) => (
-
+
🌍
{location.name} ({location.iso2})
@@ -581,9 +578,7 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
-
- {t('计费开始')}:
-
+ {t('计费开始')}:
{details.started_at
? timestamp2string(details.started_at)
@@ -591,9 +586,7 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
-
- {t('预计结束')}:
-
+ {t('预计结束')}:
{details.finished_at
? timestamp2string(details.finished_at)
@@ -651,10 +644,10 @@ const ViewDetailsModal = ({ visible, onCancel, deployment, t }) => {
}
onPress={handleRefresh}
isPending={loading || containersLoading}
>
+
{t('刷新')}
{t('关闭')}
diff --git a/web/src/components/table/models/modals/EditModelModal.jsx b/web/src/components/table/models/modals/EditModelModal.jsx
index e51839a33e8..2d56471419b 100644
--- a/web/src/components/table/models/modals/EditModelModal.jsx
+++ b/web/src/components/table/models/modals/EditModelModal.jsx
@@ -15,13 +15,7 @@ For commercial licensing, please contact support@quantumnous.com
import React, { useEffect, useState, useMemo } from 'react';
import JSONEditor from '../../../common/ui/JSONEditor';
import { Button, Card, Spinner, Switch } from '@heroui/react';
-import {
- AlertTriangle,
- ExternalLink,
- FileText,
- Save,
- X,
-} from 'lucide-react';
+import { AlertTriangle, ExternalLink, FileText, Save, X } from 'lucide-react';
import { API, showError, showSuccess } from '../../../../helpers';
import { useTranslation } from 'react-i18next';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
@@ -631,9 +625,7 @@ const EditModelModal = (props) => {
{t('参与官方同步')}
- {t(
- '关闭后,此模型将不会被"同步官方"自动覆盖或创建',
- )}
+ {t('关闭后,此模型将不会被"同步官方"自动覆盖或创建')}
{
diff --git a/web/src/components/table/models/modals/EditPrefillGroupModal.jsx b/web/src/components/table/models/modals/EditPrefillGroupModal.jsx
index 4d8e38ebc0e..aafd3bbf15a 100644
--- a/web/src/components/table/models/modals/EditPrefillGroupModal.jsx
+++ b/web/src/components/table/models/modals/EditPrefillGroupModal.jsx
@@ -93,11 +93,7 @@ function TagInput({ value = [], onChange, placeholder, ariaLabel }) {
if (event.key === 'Enter' || event.key === ',') {
event.preventDefault();
commit(draft);
- } else if (
- event.key === 'Backspace' &&
- !draft &&
- items.length > 0
- ) {
+ } else if (event.key === 'Backspace' && !draft && items.length > 0) {
remove(items.length - 1);
}
}}
@@ -367,19 +363,12 @@ const EditPrefillGroupModal = ({
diff --git a/web/src/components/table/models/modals/PrefillGroupManagement.jsx b/web/src/components/table/models/modals/PrefillGroupManagement.jsx
index f31febab9c6..125213f1abe 100644
--- a/web/src/components/table/models/modals/PrefillGroupManagement.jsx
+++ b/web/src/components/table/models/modals/PrefillGroupManagement.jsx
@@ -152,7 +152,9 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
: items || {};
const keys = Object.keys(obj);
if (keys.length === 0) {
- return {t('暂无项目')};
+ return (
+ {t('暂无项目')}
+ );
}
return renderLimitedItems({
items: keys,
@@ -171,7 +173,9 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
maxDisplay: 3,
});
} catch {
- return {t('数据格式错误')};
+ return (
+ {t('数据格式错误')}
+ );
}
},
},
@@ -182,7 +186,11 @@ const PrefillGroupManagement = ({ visible, onClose }) => {
width: 140,
render: (_, record) => (
- handleEdit(record)}>
+ handleEdit(record)}
+ >
{t('编辑')}
{
- }
- onPress={() => handleEdit()}
- >
+ handleEdit()}>
+
{t('新建组')}
diff --git a/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx b/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx
index 1b88b251f21..7293d376046 100644
--- a/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx
+++ b/web/src/components/table/redemptions/modals/EditRedemptionModal.jsx
@@ -39,7 +39,17 @@ import ConfirmDialog from '@/components/common/ui/ConfirmDialog';
const inputClass =
'h-10 w-full rounded-lg border border-[color:var(--app-border)] bg-background px-3 text-sm text-foreground outline-none transition focus:border-primary disabled:opacity-50';
-function NumberField({ label, value, onChange, min, step, prefix, helper, error, placeholder }) {
+function NumberField({
+ label,
+ value,
+ onChange,
+ min,
+ step,
+ prefix,
+ helper,
+ error,
+ placeholder,
+}) {
return (
{label}
@@ -352,10 +362,7 @@ const EditRedemptionModal = (props) => {
value={toLocalDateTimeInputValue(values.expired_time)}
onChange={(event) => {
const v = event.target.value;
- setField(
- 'expired_time',
- v ? new Date(v) : null,
- );
+ setField('expired_time', v ? new Date(v) : null);
}}
aria-label={t('过期时间')}
className={inputClass}
@@ -449,19 +456,16 @@ const EditRedemptionModal = (props) => {
diff --git a/web/src/components/table/subscriptions/modals/AddEditSubscriptionModal.jsx b/web/src/components/table/subscriptions/modals/AddEditSubscriptionModal.jsx
index be5d5307dfe..20182fdf05a 100644
--- a/web/src/components/table/subscriptions/modals/AddEditSubscriptionModal.jsx
+++ b/web/src/components/table/subscriptions/modals/AddEditSubscriptionModal.jsx
@@ -227,10 +227,7 @@ const AddEditSubscriptionModal = ({
if (!values.custom_seconds || Number(values.custom_seconds) < 1) {
next.custom_seconds = t('请输入秒数');
}
- } else if (
- !values.duration_value ||
- Number(values.duration_value) < 1
- ) {
+ } else if (!values.duration_value || Number(values.duration_value) < 1) {
next.duration_value = t('请输入数值');
}
if (
@@ -620,9 +617,7 @@ const AddEditSubscriptionModal = ({
-
+
{t('自定义秒数')}
diff --git a/web/src/components/table/task-logs/modals/AudioPreviewModal.jsx b/web/src/components/table/task-logs/modals/AudioPreviewModal.jsx
index 3abed63a63c..713803c985d 100644
--- a/web/src/components/table/task-logs/modals/AudioPreviewModal.jsx
+++ b/web/src/components/table/task-logs/modals/AudioPreviewModal.jsx
@@ -78,31 +78,25 @@ const AudioClipCard = ({ clip }) => {
)}
- {tags && (
-
- {tags}
-
- )}
+ {tags && {tags} }
{hasError ? (
-
- {t('音频无法播放')}
-
+ {t('音频无法播放')}
}
onPress={() => window.open(audioUrl, '_blank')}
variant='tertiary'
>
+
{t('在新标签页中打开')}
}
onPress={() => navigator.clipboard.writeText(audioUrl)}
variant='tertiary'
>
+
{t('复制链接')}
@@ -141,13 +135,14 @@ const AudioPreviewModal = ({ isModalOpen, setIsModalOpen, audioClips }) => {
{clips.length === 0 ? (
-
- {t('无')}
-
+ {t('无')}
) : (
{clips.map((clip, idx) => (
-
+
))}
)}
diff --git a/web/src/components/table/task-logs/modals/ContentModal.jsx b/web/src/components/table/task-logs/modals/ContentModal.jsx
index 7d13fa54b20..a786a8a6740 100644
--- a/web/src/components/table/task-logs/modals/ContentModal.jsx
+++ b/web/src/components/table/task-logs/modals/ContentModal.jsx
@@ -86,25 +86,15 @@ const ContentModal = ({
{t('• 需要特定的请求头或认证')}
-
- {t('• 防盗链保护机制')}
-
+ {t('• 防盗链保护机制')}
- }
- onPress={handleOpenInNewTab}
- size='sm'
- variant='tertiary'
- >
+
+
{t('在新标签页中打开')}
- }
- onPress={handleCopyUrl}
- size='sm'
- variant='tertiary'
- >
+
+
{t('复制链接')}
@@ -143,7 +133,9 @@ const ContentModal = ({
{isVideo ? t('视频预览') : t('内容预览')}
-
+
{isVideo ? (
renderVideoContent()
) : (
diff --git a/web/src/components/table/tokens/modals/EditTokenModal.jsx b/web/src/components/table/tokens/modals/EditTokenModal.jsx
index 5cd88144594..1c73dd99bf7 100644
--- a/web/src/components/table/tokens/modals/EditTokenModal.jsx
+++ b/web/src/components/table/tokens/modals/EditTokenModal.jsx
@@ -14,13 +14,7 @@ For commercial licensing, please contact support@quantumnous.com
import React, { useEffect, useState, useContext } from 'react';
import { Button, Card, Spinner, Switch } from '@heroui/react';
-import {
- CreditCard,
- KeyRound,
- Link as LinkIcon,
- Save,
- X,
-} from 'lucide-react';
+import { CreditCard, KeyRound, Link as LinkIcon, Save, X } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import {
API,
@@ -513,9 +507,7 @@ const EditTokenModal = (props) => {
- setField('name')(event.target.value)
- }
+ onChange={(event) => setField('name')(event.target.value)}
placeholder={t('请输入名称')}
className={inputClass}
/>
@@ -853,19 +845,12 @@ const EditTokenModal = (props) => {
diff --git a/web/src/components/table/usage-logs/modals/ParamOverrideModal.jsx b/web/src/components/table/usage-logs/modals/ParamOverrideModal.jsx
index eb79a7b14c1..bceaac1133b 100644
--- a/web/src/components/table/usage-logs/modals/ParamOverrideModal.jsx
+++ b/web/src/components/table/usage-logs/modals/ParamOverrideModal.jsx
@@ -161,12 +161,12 @@ const ParamOverrideModal = ({
}
variant='tertiary'
size='sm'
onPress={copyAll}
isDisabled={lines.length === 0}
>
+
{t('复制')}
diff --git a/web/src/components/table/users/modals/AddUserModal.jsx b/web/src/components/table/users/modals/AddUserModal.jsx
index 41c6bf92834..b57cc333987 100644
--- a/web/src/components/table/users/modals/AddUserModal.jsx
+++ b/web/src/components/table/users/modals/AddUserModal.jsx
@@ -155,13 +155,17 @@ const AddUserModal = (props) => {
setField('username')(event.target.value)}
+ onChange={(event) =>
+ setField('username')(event.target.value)
+ }
placeholder={t('请输入用户名')}
aria-label={t('用户名')}
className={inputClass}
/>
{errors.username ? (
- {errors.username}
+
+ {errors.username}
+
) : null}
@@ -189,13 +193,17 @@ const AddUserModal = (props) => {
setField('password')(event.target.value)}
+ onChange={(event) =>
+ setField('password')(event.target.value)
+ }
placeholder={t('请输入密码')}
aria-label={t('密码')}
className={inputClass}
/>
{errors.password ? (
- {errors.password}
+
+ {errors.password}
+
) : null}
@@ -218,19 +226,12 @@ const AddUserModal = (props) => {
diff --git a/web/src/components/table/users/modals/EditUserModal.jsx b/web/src/components/table/users/modals/EditUserModal.jsx
index 3af8a6c4c86..a15c5e1ef06 100644
--- a/web/src/components/table/users/modals/EditUserModal.jsx
+++ b/web/src/components/table/users/modals/EditUserModal.jsx
@@ -27,14 +27,7 @@ import {
Spinner,
useOverlayState,
} from '@heroui/react';
-import {
- Edit3,
- Link as LinkIcon,
- Save,
- User,
- Users,
- X,
-} from 'lucide-react';
+import { Edit3, Link as LinkIcon, Save, User, Users, X } from 'lucide-react';
import {
API,
showError,
@@ -325,9 +318,7 @@ const EditUserModal = (props) => {
>
-
- {t(isEdit ? '编辑' : '新建')}
-
+ {t(isEdit ? '编辑' : '新建')}
{isEdit ? t('编辑用户') : t('创建用户')}
@@ -482,9 +473,9 @@ const EditUserModal = (props) => {
{t('调整额度')}
}
onPress={() => setAdjustModalOpen(true)}
>
+
{t('调整额度')}
@@ -532,9 +523,7 @@ const EditUserModal = (props) => {
{t('绑定信息')}
- {t(
- '管理用户已绑定的第三方账户,支持筛选与解绑',
- )}
+ {t('管理用户已绑定的第三方账户,支持筛选与解绑')}
@@ -552,19 +541,12 @@ const EditUserModal = (props) => {
@@ -675,9 +657,7 @@ const EditUserModal = (props) => {
quota === ''
? ''
: adjustMode === 'override'
- ? Number(
- quotaToDisplayAmount(quota).toFixed(6),
- )
+ ? Number(quotaToDisplayAmount(quota).toFixed(6))
: Number(
quotaToDisplayAmount(
Math.abs(quota),
diff --git a/web/src/components/table/users/modals/UserBindingManagementModal.jsx b/web/src/components/table/users/modals/UserBindingManagementModal.jsx
index 1a97acd173f..80c9792975f 100644
--- a/web/src/components/table/users/modals/UserBindingManagementModal.jsx
+++ b/web/src/components/table/users/modals/UserBindingManagementModal.jsx
@@ -153,9 +153,7 @@ const UserBindingManagementModal = ({
return;
}
setCustomOAuthBindings((prev) =>
- prev.filter(
- (item) => Number(item.provider_id) !== Number(provider.id),
- ),
+ prev.filter((item) => Number(item.provider_id) !== Number(provider.id)),
);
showSuccess(t('解绑成功'));
} catch (error) {
@@ -198,9 +196,7 @@ const UserBindingManagementModal = ({
name: 'Discord',
enabled: Boolean(statusInfo.discord_oauth),
value: getBuiltInBindingValue('discord_id'),
- icon: (
-
- ),
+ icon: ,
},
{
key: 'oidc',
@@ -208,9 +204,7 @@ const UserBindingManagementModal = ({
name: 'OIDC',
enabled: Boolean(statusInfo.oidc_enabled),
value: getBuiltInBindingValue('oidc_id'),
- icon: (
-
- ),
+ icon: ,
},
{
key: 'wechat',
@@ -218,9 +212,7 @@ const UserBindingManagementModal = ({
name: t('微信'),
enabled: Boolean(statusInfo.wechat_login),
value: getBuiltInBindingValue('wechat_id'),
- icon: (
-
- ),
+ icon: ,
},
{
key: 'telegram',
@@ -228,9 +220,7 @@ const UserBindingManagementModal = ({
name: 'Telegram',
enabled: Boolean(statusInfo.telegram_oauth),
value: getBuiltInBindingValue('telegram_id'),
- icon: (
-
- ),
+ icon: ,
},
{
key: 'linuxdo',
@@ -238,9 +228,7 @@ const UserBindingManagementModal = ({
name: 'LinuxDO',
enabled: Boolean(statusInfo.linuxdo_oauth),
value: getBuiltInBindingValue('linux_do_id'),
- icon: (
-
- ),
+ icon: ,
},
];
@@ -306,10 +294,7 @@ const UserBindingManagementModal = ({
<>
-
+
@@ -394,7 +379,6 @@ const UserBindingManagementModal = ({
color='danger'
variant='tertiary'
size='sm'
- startContent={ }
isDisabled={!isBound}
isPending={Boolean(
bindingActionLoading[loadingKey],
@@ -418,6 +402,7 @@ const UserBindingManagementModal = ({
}
}}
>
+
{t('解绑')}
@@ -445,7 +430,8 @@ const UserBindingManagementModal = ({
const target = pendingUnbind;
setPendingUnbind(null);
if (!target) return;
- if (target.kind === 'builtin') performUnbindBuiltIn(target.bindingItem);
+ if (target.kind === 'builtin')
+ performUnbindBuiltIn(target.bindingItem);
else performUnbindCustom(target.provider);
}}
>
diff --git a/web/src/components/table/users/modals/UserSubscriptionsModal.jsx b/web/src/components/table/users/modals/UserSubscriptionsModal.jsx
index 66e76160a6d..db6f4b8a880 100644
--- a/web/src/components/table/users/modals/UserSubscriptionsModal.jsx
+++ b/web/src/components/table/users/modals/UserSubscriptionsModal.jsx
@@ -63,13 +63,7 @@ function renderStatusTag(sub, t) {
return {t('已过期')};
}
-const UserSubscriptionsModal = ({
- visible,
- onCancel,
- user,
- t,
- onSuccess,
-}) => {
+const UserSubscriptionsModal = ({ visible, onCancel, user, t, onSuccess }) => {
const isMobile = useIsMobile();
const [loading, setLoading] = useState(false);
const [creating, setCreating] = useState(false);
@@ -421,10 +415,10 @@ const UserSubscriptionsModal = ({
}
isPending={creating}
onPress={createSubscription}
>
+
{t('新增订阅')}
diff --git a/web/src/components/topup/RechargeCard.jsx b/web/src/components/topup/RechargeCard.jsx
index ef59da83c59..a8546e2784a 100644
--- a/web/src/components/topup/RechargeCard.jsx
+++ b/web/src/components/topup/RechargeCard.jsx
@@ -193,14 +193,10 @@ const RechargeCard = ({
const minTopupVal = Number(payMethod.min_topup) || 0;
const isStripe = payMethod.type === 'stripe';
const isWaffo =
- typeof payMethod.type === 'string' &&
- payMethod.type.startsWith('waffo:');
+ typeof payMethod.type === 'string' && payMethod.type.startsWith('waffo:');
const isWaffoPancake = payMethod.type === 'waffo_pancake';
const disabled =
- (!enableOnlineTopUp &&
- !isStripe &&
- !isWaffo &&
- !isWaffoPancake) ||
+ (!enableOnlineTopUp && !isStripe && !isWaffo && !isWaffoPancake) ||
(!enableStripeTopUp && isStripe) ||
(!enableWaffoTopUp && isWaffo) ||
(!enableWaffoPancakeTopUp && isWaffoPancake) ||
@@ -234,12 +230,12 @@ const RechargeCard = ({
preTopUp(payMethod.type)}
isDisabled={disabled}
isPending={paymentLoading && payWay === payMethod.type}
className='!rounded-lg !px-4 !py-2'
>
+ {startIcon}
{payMethod.name}
);
@@ -254,9 +250,7 @@ const RechargeCard = ({
);
}
- return (
- {buttonEl}
- );
+ return {buttonEl};
};
const renderPresetGrid = () => (
@@ -278,9 +272,7 @@ const RechargeCard = ({
{presetAmounts.map((preset, index) => {
const discount =
- preset.discount ||
- topupInfo?.discount?.[preset.value] ||
- 1.0;
+ preset.discount || topupInfo?.discount?.[preset.value] || 1.0;
const originalPrice = preset.value * priceRatio;
const discountedPrice = originalPrice * discount;
const hasDiscount = discount < 1.0;
@@ -324,9 +316,7 @@ const RechargeCard = ({
setTopUpCount(preset.value);
}}
className={`flex w-full cursor-pointer flex-col items-center gap-1 rounded-xl bg-background px-3 py-3 text-center transition-colors hover:border-primary ${
- selected
- ? 'border-2 border-primary'
- : 'border border-border'
+ selected ? 'border-2 border-primary' : 'border border-border'
}`}
>
@@ -551,11 +541,8 @@ const RechargeCard = ({
- }
- onPress={onOpenHistory}
- >
+
+
{t('账单')}
diff --git a/web/src/components/topup/modals/SubscriptionPurchaseModal.jsx b/web/src/components/topup/modals/SubscriptionPurchaseModal.jsx
index 3b43a90de0b..ec005e77cd8 100644
--- a/web/src/components/topup/modals/SubscriptionPurchaseModal.jsx
+++ b/web/src/components/topup/modals/SubscriptionPurchaseModal.jsx
@@ -96,17 +96,24 @@ const SubscriptionPurchaseModal = ({
-
+
-
+
{formatSubscriptionDuration(plan, t)}
}
/>
- {formatSubscriptionResetPeriod(plan, t) !== t('不重置') && (
+ {formatSubscriptionResetPeriod(plan, t) !==
+ t('不重置') && (
{totalAmount > 0 ? (
-
+
{renderQuota(totalAmount)}
) : (
@@ -164,11 +173,11 @@ const SubscriptionPurchaseModal = ({
}
onPress={onPayStripe}
isPending={paying}
isDisabled={purchaseLimitReached}
>
+
Stripe
)}
@@ -176,11 +185,11 @@ const SubscriptionPurchaseModal = ({
}
onPress={onPayCreem}
isPending={paying}
isDisabled={purchaseLimitReached}
>
+
Creem
)}
@@ -208,7 +217,9 @@ const SubscriptionPurchaseModal = ({
color='primary'
onPress={onPayEpay}
isPending={paying}
- isDisabled={!selectedEpayMethod || purchaseLimitReached}
+ isDisabled={
+ !selectedEpayMethod || purchaseLimitReached
+ }
>
{t('支付')}
diff --git a/web/src/components/topup/modals/TopupHistoryModal.jsx b/web/src/components/topup/modals/TopupHistoryModal.jsx
index 4ddfa2a03a3..2bfcae4176c 100644
--- a/web/src/components/topup/modals/TopupHistoryModal.jsx
+++ b/web/src/components/topup/modals/TopupHistoryModal.jsx
@@ -32,7 +32,13 @@ import {
useOverlayState,
} from '@heroui/react';
import { Coins, Search } from 'lucide-react';
-import { API, copy, showError, showSuccess, timestamp2string } from '../../../helpers';
+import {
+ API,
+ copy,
+ showError,
+ showSuccess,
+ timestamp2string,
+} from '../../../helpers';
import { isAdmin } from '../../../helpers/utils';
import { useIsMobile } from '../../../hooks/common/useIsMobile';
import ConfirmDialog from '../../common/ui/ConfirmDialog';
@@ -145,7 +151,11 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => {
primary: 'primary',
};
return (
-
+
{t(config.key)}
);
@@ -218,7 +228,9 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => {
dataIndex: 'money',
key: 'money',
render: (money) => (
- ¥{Number(money || 0).toFixed(2)}
+
+ ¥{Number(money || 0).toFixed(2)}
+
),
},
{
@@ -239,13 +251,13 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => {
if (record.status === 'pending') {
actions.push(
confirmAdminComplete(record.trade_no)}
>
{t('补单')}
-
+ ,
);
}
return actions.length > 0 ? <>{actions}> : null;
@@ -286,13 +298,14 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => {
}
placeholder={t('订单号')}
value={keyword}
onValueChange={handleKeywordChange}
isClearable
size='sm'
- />
+ >
+
+
{loading ? (
@@ -336,7 +349,10 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => {
{columns.map((column) => (
- |
+ |
{column.title}
|
))}
@@ -349,7 +365,10 @@ const TopupHistoryModal = ({ visible, onCancel, t }) => {
className='bg-background transition hover:bg-surface-secondary/60'
>
{columns.map((column) => (
-
+ |
{renderCell(record, column) || '-'}
|
))}
diff --git a/web/src/pages/Home/index.jsx b/web/src/pages/Home/index.jsx
index d55b6d237a9..dd3c4160375 100644
--- a/web/src/pages/Home/index.jsx
+++ b/web/src/pages/Home/index.jsx
@@ -211,7 +211,6 @@ const Home = () => {
size={isMobile ? 'md' : 'lg'}
variant='tertiary'
className='rounded-full px-6 py-2 font-semibold'
- startContent={}
onPress={() =>
window.open(
'https://github.com/QuantumNous/new-api',
@@ -219,6 +218,7 @@ const Home = () => {
)
}
>
+
{statusState.status.version}
) : (
@@ -227,9 +227,9 @@ const Home = () => {
size={isMobile ? 'md' : 'lg'}
variant='tertiary'
className='rounded-full px-6 py-2 font-semibold'
- startContent={}
onPress={() => window.open(docsLink, '_blank')}
>
+
{t('文档')}
)
diff --git a/web/src/pages/Setting/Chat/SettingsChats.jsx b/web/src/pages/Setting/Chat/SettingsChats.jsx
index c7f3dfc6bf9..efa3ed7750b 100644
--- a/web/src/pages/Setting/Chat/SettingsChats.jsx
+++ b/web/src/pages/Setting/Chat/SettingsChats.jsx
@@ -176,8 +176,7 @@ export default function SettingsChats(props) {
return;
}
const updateArray = compareObjects(inputs, inputsRow);
- if (!updateArray.length)
- return showWarning(t('你似乎并没有修改什么'));
+ if (!updateArray.length) return showWarning(t('你似乎并没有修改什么'));
const requestQueue = updateArray.map((item) => {
let value = '';
if (typeof inputs[item.key] === 'boolean') {
@@ -297,9 +296,7 @@ export default function SettingsChats(props) {
syncConfigsToJson(newConfigs);
} else {
const maxId =
- chatConfigs.length > 0
- ? Math.max(...chatConfigs.map((c) => c.id))
- : -1;
+ chatConfigs.length > 0 ? Math.max(...chatConfigs.map((c) => c.id)) : -1;
const newConfig = { id: maxId + 1, ...values };
const newConfigs = [...chatConfigs, newConfig];
setChatConfigs(newConfigs);
@@ -335,10 +332,7 @@ export default function SettingsChats(props) {
const pageStart = (page - 1) * PAGE_SIZE;
const pageItems = filteredConfigs.slice(pageStart, pageStart + PAGE_SIZE);
- const pageCount = Math.max(
- 1,
- Math.ceil(filteredConfigs.length / PAGE_SIZE),
- );
+ const pageCount = Math.max(1, Math.ceil(filteredConfigs.length / PAGE_SIZE));
const highlightKeywords = (text) => {
if (!text) return text;
@@ -429,31 +423,23 @@ export default function SettingsChats(props) {
{editMode === 'visual' ? (
- }
- onPress={handleAddConfig}
- >
+
+
{t('添加聊天配置')}
}
- endContent={ }
- >
+
+
{t('填入模板')}
+
}
/>
- }
- onPress={onSubmit}
- >
+
+
{t('保存聊天设置')}
@@ -515,17 +501,17 @@ export default function SettingsChats(props) {
}
onPress={() => handleEditConfig(record)}
>
+
{t('编辑')}
}
onPress={() => handleDeleteConfig(record.id)}
>
+
{t('删除')}
@@ -565,9 +551,7 @@ export default function SettingsChats(props) {
size='sm'
variant='tertiary'
isDisabled={page >= pageCount}
- onPress={() =>
- setPage((p) => Math.min(pageCount, p + 1))
- }
+ onPress={() => setPage((p) => Math.min(pageCount, p + 1))}
>
{t('下一页')}
@@ -603,11 +587,8 @@ export default function SettingsChats(props) {
{editMode === 'json' && (
- }
- onPress={onSubmit}
- >
+
+
{t('保存聊天设置')}
@@ -618,9 +599,7 @@ export default function SettingsChats(props) {
-
- {isEdit ? t('编辑聊天配置') : t('添加聊天配置')}
-
+ {isEdit ? t('编辑聊天配置') : t('添加聊天配置')}
diff --git a/web/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx b/web/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx
index ee6d279f9a4..33c56746a12 100644
--- a/web/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx
+++ b/web/src/pages/Setting/Dashboard/SettingsAPIInfo.jsx
@@ -355,30 +355,30 @@ const SettingsAPIInfo = ({ options, refresh }) => {
}
onPress={handleAddApi}
className='w-full md:w-auto'
>
+
{t('添加API')}
}
isDisabled={selectedRowKeys.length === 0}
onPress={handleBatchDelete}
className='w-full md:w-auto'
>
+
{t('批量删除')}{' '}
{selectedRowKeys.length > 0 ? `(${selectedRowKeys.length})` : ''}
}
onPress={submitApiInfo}
isPending={loading}
isDisabled={!hasChanges}
className='w-full md:w-auto'
>
+
{t('保存设置')}
@@ -486,17 +486,17 @@ const SettingsAPIInfo = ({ options, refresh }) => {
}
onPress={() => handleEditApi(record)}
>
+
{t('编辑')}
}
onPress={() => setPendingDelete(record)}
>
+
{t('删除')}
@@ -545,9 +545,7 @@ const SettingsAPIInfo = ({ options, refresh }) => {
size='sm'
variant='tertiary'
isDisabled={currentPage >= totalPages}
- onPress={() =>
- setCurrentPage((p) => Math.min(totalPages, p + 1))
- }
+ onPress={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
>
{t('下一页')}
@@ -662,7 +660,10 @@ const SettingsAPIInfo = ({ options, refresh }) => {
- setShowApiModal(false)}>
+ setShowApiModal(false)}
+ >
{t('取消')}
{
const handleSaveAnnouncement = async () => {
const next = {};
- if (!announcementForm.content?.trim())
- next.content = t('请输入公告内容');
+ if (!announcementForm.content?.trim()) next.content = t('请输入公告内容');
if (!announcementForm.publishDate) next.publishDate = t('请选择发布日期');
setFormErrors(next);
if (Object.keys(next).length > 0) return;
@@ -384,30 +376,30 @@ const SettingsAnnouncements = ({ options, refresh }) => {
}
onPress={handleAddAnnouncement}
className='w-full md:w-auto'
>
+
{t('添加公告')}
}
isDisabled={selectedRowKeys.length === 0}
onPress={handleBatchDelete}
className='w-full md:w-auto'
>
+
{t('批量删除')}{' '}
{selectedRowKeys.length > 0 ? `(${selectedRowKeys.length})` : ''}
}
onPress={submitAnnouncements}
isPending={loading}
isDisabled={!hasChanges}
className='w-full md:w-auto'
>
+
{t('保存设置')}
@@ -441,18 +433,14 @@ const SettingsAnnouncements = ({ options, refresh }) => {
ariaLabel={t('选择当前页')}
/>
-
- {t('内容')}
- |
+ {t('内容')} |
{t('发布时间')}
|
{t('类型')}
|
-
- {t('说明')}
- |
+ {t('说明')} |
{t('操作')}
|
@@ -525,17 +513,17 @@ const SettingsAnnouncements = ({ options, refresh }) => {
}
onPress={() => handleEditAnnouncement(record)}
>
+
{t('编辑')}
}
onPress={() => setPendingDelete(record)}
>
+
{t('删除')}
@@ -566,7 +554,9 @@ const SettingsAnnouncements = ({ options, refresh }) => {
))}
- {t('共 {{total}} 条', { total: announcementsList.length })}
+
+ {t('共 {{total}} 条', { total: announcementsList.length })}
+
{
size='sm'
variant='tertiary'
isDisabled={currentPage >= totalPages}
- onPress={() =>
- setCurrentPage((p) => Math.min(totalPages, p + 1))
- }
+ onPress={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
>
{t('下一页')}
@@ -610,9 +598,9 @@ const SettingsAnnouncements = ({ options, refresh }) => {
}
onPress={() => setShowContentModal(true)}
>
+
{t('放大编辑')}
@@ -644,7 +632,9 @@ const SettingsAnnouncements = ({ options, refresh }) => {
{
const v = event.target.value;
setAnnouncementForm((prev) => ({
@@ -748,7 +738,10 @@ const SettingsAnnouncements = ({ options, refresh }) => {
/>
- setShowContentModal(false)}>
+ setShowContentModal(false)}
+ >
{t('取消')}
{
}
onPress={handleAddFaq}
className='w-full md:w-auto'
>
+
{t('添加问答')}
}
isDisabled={selectedRowKeys.length === 0}
onPress={handleBatchDelete}
className='w-full md:w-auto'
>
+
{t('批量删除')}{' '}
{selectedRowKeys.length > 0 ? `(${selectedRowKeys.length})` : ''}
}
onPress={submitFAQ}
isPending={loading}
isDisabled={!hasChanges}
className='w-full md:w-auto'
>
+
{t('保存设置')}
@@ -405,17 +399,17 @@ const SettingsFAQ = ({ options, refresh }) => {
}
onPress={() => handleEditFaq(record)}
>
+
{t('编辑')}
}
onPress={() => setPendingDelete(record)}
>
+
{t('删除')}
@@ -464,9 +458,7 @@ const SettingsFAQ = ({ options, refresh }) => {
size='sm'
variant='tertiary'
isDisabled={currentPage >= totalPages}
- onPress={() =>
- setCurrentPage((p) => Math.min(totalPages, p + 1))
- }
+ onPress={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
>
{t('下一页')}
@@ -534,7 +526,10 @@ const SettingsFAQ = ({ options, refresh }) => {
- setShowFaqModal(false)}>
+ setShowFaqModal(false)}
+ >
{t('取消')}
{
const handleSaveGroup = async () => {
const next = {};
- if (!uptimeForm.categoryName?.trim()) next.categoryName = t('请输入分类名称');
+ if (!uptimeForm.categoryName?.trim())
+ next.categoryName = t('请输入分类名称');
if (!uptimeForm.url?.trim()) {
next.url = t('请输入Uptime Kuma地址');
} else {
@@ -301,30 +302,30 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
}
onPress={handleAddGroup}
className='w-full md:w-auto'
>
+
{t('添加分类')}
}
isDisabled={selectedRowKeys.length === 0}
onPress={handleBatchDelete}
className='w-full md:w-auto'
>
+
{t('批量删除')}{' '}
{selectedRowKeys.length > 0 ? `(${selectedRowKeys.length})` : ''}
}
onPress={submitUptimeGroups}
isPending={loading}
isDisabled={!hasChanges}
className='w-full md:w-auto'
>
+
{t('保存设置')}
@@ -419,17 +420,17 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
}
onPress={() => handleEditGroup(record)}
>
+
{t('编辑')}
}
onPress={() => setPendingDelete(record)}
>
+
{t('删除')}
@@ -460,7 +461,9 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
))}
- {t('共 {{total}} 条', { total: uptimeGroupsList.length })}
+
+ {t('共 {{total}} 条', { total: uptimeGroupsList.length })}
+
{
size='sm'
variant='tertiary'
isDisabled={currentPage >= totalPages}
- onPress={() =>
- setCurrentPage((p) => Math.min(totalPages, p + 1))
- }
+ onPress={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
>
{t('下一页')}
@@ -567,7 +568,9 @@ const SettingsUptimeKuma = ({ options, refresh }) => {
className={inputClass}
/>
{formErrors.slug ? (
- {formErrors.slug}
+
+ {formErrors.slug}
+
) : null}
diff --git a/web/src/pages/Setting/Model/SettingModelDeployment.jsx b/web/src/pages/Setting/Model/SettingModelDeployment.jsx
index dadacbd5e54..449db02296a 100644
--- a/web/src/pages/Setting/Model/SettingModelDeployment.jsx
+++ b/web/src/pages/Setting/Model/SettingModelDeployment.jsx
@@ -183,9 +183,7 @@ export default function SettingModelDeployment(props) {
onChange={(e) =>
setField('model_deployment.ionet.api_key')(e.target.value)
}
- placeholder={t(
- '请输入 io.net API Key(敏感信息不显示)',
- )}
+ placeholder={t('请输入 io.net API Key(敏感信息不显示)')}
disabled={!enabled}
aria-label={t('API Key')}
className={inputClass}
@@ -199,11 +197,11 @@ export default function SettingModelDeployment(props) {
}
onPress={testApiKey}
isPending={testing}
isDisabled={!enabled}
>
+
{testing ? t('连接测试中...') : t('测试连接')}
@@ -223,13 +221,13 @@ export default function SettingModelDeployment(props) {
}
onPress={() =>
window.open('https://ai.io.net/ai/api-keys', '_blank')
}
className='w-full'
>
{t('前往 io.net API Keys')}
+
diff --git a/web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx b/web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx
index 806dcdb8f3b..5119ac00dd1 100644
--- a/web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx
+++ b/web/src/pages/Setting/Operation/SettingsChannelAffinity.jsx
@@ -239,7 +239,8 @@ const tryParseRulesJsonArray = (jsonString) => {
const parseOptionalObjectJson = (jsonString, label) => {
const raw = (jsonString || '').trim();
if (!raw) return { ok: true, value: null };
- if (!verifyJSON(raw)) return { ok: false, message: `${label} JSON 格式不正确` };
+ if (!verifyJSON(raw))
+ return { ok: false, message: `${label} JSON 格式不正确` };
try {
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
@@ -583,7 +584,9 @@ export default function SettingsChannelAffinity(props) {
return showError(t(keySourcesValidation.message));
}
- const userAgentInclude = normalizeStringList(values.user_agent_include_text);
+ const userAgentInclude = normalizeStringList(
+ values.user_agent_include_text,
+ );
const paramTemplateValidation = parseOptionalObjectJson(
paramTemplateDraft,
'参数覆盖模板',
@@ -743,12 +746,8 @@ export default function SettingsChannelAffinity(props) {
| {t('名称')} |
-
- {t('模型正则')}
- |
-
- {t('路径正则')}
- |
+ {t('模型正则')} |
+ {t('路径正则')} |
{t('Key 来源')} |
{t('TTL(秒)')}
@@ -798,10 +797,7 @@ export default function SettingsChannelAffinity(props) {
{(record.model_regex || []).length === 0
? '-'
: (record.model_regex || []).slice(0, 3).map((v, idx) => (
-
+
{v}
))}
@@ -810,10 +806,7 @@ export default function SettingsChannelAffinity(props) {
{(record.path_regex || []).length === 0
? '-'
: (record.path_regex || []).slice(0, 2).map((v, idx) => (
-
+
{v}
))}
@@ -821,18 +814,20 @@ export default function SettingsChannelAffinity(props) {
|
{(record.key_sources || []).length === 0
? '-'
- : (record.key_sources || []).slice(0, 3).map((src, idx) => {
- const s = normalizeKeySource(src);
- const detail = s.type === 'gjson' ? s.path : s.key;
- return (
-
- {s.type}:{detail}
-
- );
- })}
+ : (record.key_sources || [])
+ .slice(0, 3)
+ .map((src, idx) => {
+ const s = normalizeKeySource(src);
+ const detail = s.type === 'gjson' ? s.path : s.key;
+ return (
+
+ {s.type}:{detail}
+
+ );
+ })}
|
{Number(record.ttl_seconds || 0) || '-'}
@@ -849,9 +844,9 @@ export default function SettingsChannelAffinity(props) {
}
onPress={() => openParamTemplatePreview(record)}
>
+
{t('预览模板')}
) : (
@@ -1042,17 +1037,13 @@ export default function SettingsChannelAffinity(props) {
value={inputs[KEY_MAX_ENTRIES] ?? 0}
onChange={(event) => {
const raw = event.target.value;
- setInputsField(KEY_MAX_ENTRIES)(
- raw === '' ? 0 : Number(raw),
- );
+ setInputsField(KEY_MAX_ENTRIES)(raw === '' ? 0 : Number(raw));
}}
placeholder='例如 100000…'
className={inputClass}
/>
- {t(
- '内存缓存最大条目数。0 表示使用后端默认容量:100000。',
- )}
+ {t('内存缓存最大条目数。0 表示使用后端默认容量:100000。')}
@@ -1063,9 +1054,7 @@ export default function SettingsChannelAffinity(props) {
value={inputs[KEY_DEFAULT_TTL] ?? 0}
onChange={(event) => {
const raw = event.target.value;
- setInputsField(KEY_DEFAULT_TTL)(
- raw === '' ? 0 : Number(raw),
- );
+ setInputsField(KEY_DEFAULT_TTL)(raw === '' ? 0 : Number(raw));
}}
placeholder='例如 3600…'
className={inputClass}
@@ -1121,11 +1110,8 @@ export default function SettingsChannelAffinity(props) {
{t('填充 Codex CLI / Claude CLI 模版')}
- }
- onPress={openAddModal}
- >
+
+
{t('新增规则')}
@@ -1133,10 +1119,10 @@ export default function SettingsChannelAffinity(props) {
}
isPending={cacheLoading}
onPress={refreshCacheStats}
>
+
{t('刷新缓存统计')}
setConfirmClearAll(true)}>
@@ -1231,9 +1217,7 @@ export default function SettingsChannelAffinity(props) {
className={textareaClass}
/>
- {t(
- '可选。对请求路径进行匹配;不填表示匹配所有路径。',
- )}
+ {t('可选。对请求路径进行匹配;不填表示匹配所有路径。')}
@@ -1312,9 +1296,7 @@ export default function SettingsChannelAffinity(props) {
-
- {t('TTL(秒,0 表示默认)')}
-
+ {t('TTL(秒,0 表示默认)')}
}
onPress={() =>
setParamTemplateEditorVisible(true)
}
>
+
{t('可视化编辑')}
{t('Key 来源')}
- }
- onPress={addKeySource}
- >
+
+
{t('新增 Key 来源')}
@@ -1501,10 +1479,9 @@ export default function SettingsChannelAffinity(props) {
onConfirm={async () => {
setConfirmClearAll(false);
try {
- const res = await API.delete(
- '/api/option/channel_affinity_cache',
- { params: { all: true } },
- );
+ const res = await API.delete('/api/option/channel_affinity_cache', {
+ params: { all: true },
+ });
const { success, message } = res.data;
if (!success) return showError(t(message));
showSuccess(t('已清空'));
@@ -1535,10 +1512,9 @@ export default function SettingsChannelAffinity(props) {
return;
}
try {
- const res = await API.delete(
- '/api/option/channel_affinity_cache',
- { params: { rule_name: target.name } },
- );
+ const res = await API.delete('/api/option/channel_affinity_cache', {
+ params: { rule_name: target.name },
+ });
const { success, message } = res.data;
if (!success) return showError(t(message));
showSuccess(t('已清空'));
@@ -1548,9 +1524,7 @@ export default function SettingsChannelAffinity(props) {
}
}}
>
- {confirmClearRule?.name
- ? `${t('规则')}: ${confirmClearRule.name}`
- : ''}
+ {confirmClearRule?.name ? `${t('规则')}: ${confirmClearRule.name}` : ''}
{label}
@@ -288,12 +296,8 @@ export default function SettingsPaymentGatewayCreem(props) {
{t('产品配置')}
- }
- onPress={() => openProductModal()}
- >
+ openProductModal()}>
+
{t('添加产品')}
diff --git a/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx b/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx
index c996c6efd2c..07d0ef8dde1 100644
--- a/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx
+++ b/web/src/pages/Setting/Ratio/GroupRatioSettings.jsx
@@ -12,7 +12,13 @@ along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
-import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
+import React, {
+ useCallback,
+ useEffect,
+ useMemo,
+ useRef,
+ useState,
+} from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Spinner, Switch } from '@heroui/react';
import { ChevronDown, HelpCircle, X } from 'lucide-react';
@@ -174,14 +180,11 @@ export default function GroupRatioSettings(props) {
if (
!validateJSON(inputs['group_ratio_setting.group_special_usable_group'])
) {
- next['group_ratio_setting.group_special_usable_group'] = t(
- '不是合法的 JSON 字符串',
- );
+ next['group_ratio_setting.group_special_usable_group'] =
+ t('不是合法的 JSON 字符串');
}
if (!validateAutoGroups(inputs.AutoGroups)) {
- next.AutoGroups = t(
- '必须是有效的 JSON 字符串数组,例如:["g1","g2"]',
- );
+ next.AutoGroups = t('必须是有效的 JSON 字符串数组,例如:["g1","g2"]');
}
setErrors(next);
return Object.keys(next).length === 0;
@@ -281,7 +284,9 @@ export default function GroupRatioSettings(props) {
- {t('倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组')}
+ {t(
+ '倍率用于计费乘数,勾选「用户可选」后用户可在创建令牌时选择该分组',
+ )}
- {t('用户分组')}{' — '}
+ {t('用户分组')}
+ {' — '}
{t('由管理员分配,决定用户身份等级(如 default、vip)。')}
- {t('令牌分组')}{' — '}
+ {t('令牌分组')}
+ {' — '}
{t(
'用户创建令牌时选择的分组,决定该令牌的实际计费倍率。一个用户可以创建多个令牌,使用不同分组。',
)}
- {t('倍率')}{' — '}
+ {t('倍率')}
+ {' — '}
{t('计费乘数,倍率越低费用越低。例如倍率 0.5 表示半价。')}
- {t('用户可选')}{' — '}
+ {t('用户可选')}
+ {' — '}
{t(
'勾选后,该分组会出现在用户创建令牌时的下拉菜单中。未勾选的分组只能由管理员分配,用户自己无法选择。',
)}
- {t('自动分组')}{' — '}
+ {t('自动分组')}
+ {' — '}
{t(
'令牌分组设为 auto 时,系统按优先级顺序自动选择一个可用分组。',
)}
@@ -621,7 +631,9 @@ export default function GroupRatioSettings(props) {
{`${t('分组名')} ${t('倍率')} ${t('用户可选')} ${t('说明')}\n──────────────────────────────────────\nstandard 1.0 ${t('是')} ${t('标准价格')}\npremium 0.5 ${t('是')} ${t('高级套餐,半价优惠')}`}
- {t('两个分组都勾选了「用户可选」,所以用户创建令牌时可以看到这两个选项:')}
+ {t(
+ '两个分组都勾选了「用户可选」,所以用户创建令牌时可以看到这两个选项:',
+ )}
{t('用户创建令牌 → 选择分组下拉框:')}
@@ -631,7 +643,9 @@ export default function GroupRatioSettings(props) {
{` └─ premium (${t('高级套餐,半价优惠')})`}
- {t('选择 premium 创建的令牌,调用 API 时费用为 standard 的 50%。')}
+ {t(
+ '选择 premium 创建的令牌,调用 API 时费用为 standard 的 50%。',
+ )}
{t('对比:不勾选「用户可选」的场景')}
@@ -663,18 +677,22 @@ export default function GroupRatioSettings(props) {
{t('用户分组的联动作用')}
- {t('管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:')}
+ {t(
+ '管理员给用户分配的分组(如 vip)不仅决定用户身份,还会影响后续两个功能:',
+ )}
{'1. '}
- {t('特殊倍率')}{' — '}
+ {t('特殊倍率')}
+ {' — '}
{t(
'可以根据用户分组设置不同的计费倍率。例如 vip 用户使用 standard 令牌时倍率从 1.0 降为 0.8。',
)}
{'2. '}
- {t('可用分组')}{' — '}
+ {t('可用分组')}
+ {' — '}
{t(
'可以根据用户分组增减令牌可选的分组范围。例如 vip 用户额外开放 premium 分组,或移除某个分组的选择权。',
)}
@@ -728,7 +746,9 @@ export default function GroupRatioSettings(props) {
{`1. default ${t('最高优先级')}\n2. vip`}
- {t('开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。')}
+ {t(
+ '开启「默认使用 auto 分组」后,新建令牌和初始令牌都会自动设为 auto。',
+ )}
@@ -756,7 +776,9 @@ export default function GroupRatioSettings(props) {
)}
- {t('简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。')}
+ {t(
+ '简单来说:同一个令牌分组,不同等级的用户可以享受不同的价格。',
+ )}
@@ -777,14 +799,14 @@ export default function GroupRatioSettings(props) {
{`${t('用户分组')} ${t('使用分组')} ${t('倍率')}\n────────────────────────────\nvip standard 0.8\nvip premium 0.3`}
-
- {t('配置后的效果:')}
-
+ {t('配置后的效果:')}
{`${t('普通用户')} + standard ${t('令牌')} → ${t('倍率')} 1.0 (${t('不变')})\nvip ${t('用户')} + standard ${t('令牌')} → ${t('倍率')} 0.8 (${t('享受 8 折')})\nvip ${t('用户')} + premium ${t('令牌')} → ${t('倍率')} 0.3 (${t('从 0.5 降到 0.3')})`}
- {t('只有配置了规则的组合才会覆盖,未配置的组合仍使用令牌分组的基础倍率。')}
+ {t(
+ '只有配置了规则的组合才会覆盖,未配置的组合仍使用令牌分组的基础倍率。',
+ )}
@@ -812,7 +834,9 @@ export default function GroupRatioSettings(props) {
)}
- {t('通过此功能,可以根据用户所在分组,为不同等级的用户展示不同的可选列表。')}
+ {t(
+ '通过此功能,可以根据用户所在分组,为不同等级的用户展示不同的可选列表。',
+ )}
@@ -822,7 +846,9 @@ export default function GroupRatioSettings(props) {
)}
- {t('不配置规则时,所有用户看到的下拉框一样:')}
+
+ {t('不配置规则时,所有用户看到的下拉框一样:')}
+
{`${t('所有用户')} → ${t('创建令牌可选')}:\n ├─ standard\n └─ premium`}
@@ -833,9 +859,7 @@ export default function GroupRatioSettings(props) {
{`${t('用户分组')} ${t('操作')} ${t('目标分组')} ${t('描述')}\n──────────────────────────────────────────\nvip ${t('添加')} (+:) exclusive ${t('专属分组')}\nvip ${t('移除')} (-:) standard -`}
-
- {t('配置后的效果:')}
-
+ {t('配置后的效果:')}
{`${t('普通用户')} → ${t('创建令牌可选')}:\n ├─ standard\n └─ premium\n\nvip ${t('用户')} → ${t('创建令牌可选')}:\n ├─ premium (${t('保留')})\n └─ exclusive (${t('新增')})\n\n ${t('standard 已被移除,vip 用户看不到')}`}
@@ -851,12 +875,16 @@ export default function GroupRatioSettings(props) {
- group_special_usable_group
+
+ group_special_usable_group
+
{`{\n "vip": {\n "+:exclusive": "${t('专属分组')}",\n "-:standard": "remove"\n }\n}`}
- {t('键的前缀 +: 表示添加,-: 表示移除,无前缀表示追加。值为分组描述(移除时填 "remove")。')}
+ {t(
+ '键的前缀 +: 表示添加,-: 表示移除,无前缀表示追加。值为分组描述(移除时填 "remove")。',
+ )}
@@ -897,12 +925,8 @@ export default function GroupRatioSettings(props) {
);
})}
- }
- onPress={() => setShowGuide(true)}
- >
+ setShowGuide(true)}>
+
{t('使用说明')}
diff --git a/web/src/pages/Setting/Ratio/UpstreamRatioSync.jsx b/web/src/pages/Setting/Ratio/UpstreamRatioSync.jsx
index f1d618fcfcd..ac26d9c2bd8 100644
--- a/web/src/pages/Setting/Ratio/UpstreamRatioSync.jsx
+++ b/web/src/pages/Setting/Ratio/UpstreamRatioSync.jsx
@@ -57,13 +57,7 @@ import ChannelSelectorModal from '../../../components/settings/ChannelSelectorMo
const inputClass =
'h-10 w-full rounded-xl border border-border bg-background px-3 text-sm text-foreground outline-none transition focus:border-primary disabled:opacity-50';
-function StatusChip({
- tone = 'grey',
- bg,
- color,
- prefixIcon,
- children,
-}) {
+function StatusChip({ tone = 'grey', bg, color, prefixIcon, children }) {
if (bg) {
return (
{
Object.keys(ratios).forEach((ratioType) => {
- if (
- newDifferences[model] &&
- newDifferences[model][ratioType]
- ) {
+ if (newDifferences[model] && newDifferences[model][ratioType]) {
delete newDifferences[model][ratioType];
if (Object.keys(newDifferences[model]).length === 0) {
delete newDifferences[model];
@@ -566,9 +557,7 @@ export default function UpstreamRatioSync(props) {
return dataSource.filter((item) => {
const matchesKeyword =
!searchKeyword.trim() ||
- item.model
- .toLowerCase()
- .includes(searchKeyword.toLowerCase().trim());
+ item.model.toLowerCase().includes(searchKeyword.toLowerCase().trim());
const matchesRatioType =
!ratioTypeFilter || item.ratioType === ratioTypeFilter;
return matchesKeyword && matchesRatioType;
@@ -604,10 +593,8 @@ export default function UpstreamRatioSync(props) {
map[upName] = {
selectableCount,
selectedCount,
- allSelected:
- selectableCount > 0 && selectedCount === selectableCount,
- partiallySelected:
- selectedCount > 0 && selectedCount < selectableCount,
+ allSelected: selectableCount > 0 && selectedCount === selectableCount,
+ partiallySelected: selectedCount > 0 && selectedCount < selectableCount,
hasSelectableItems: selectableCount > 0,
};
});
@@ -624,10 +611,7 @@ export default function UpstreamRatioSync(props) {
);
const startIndex =
filteredDataSource.length === 0 ? 0 : (currentPage - 1) * pageSize + 1;
- const endIndex = Math.min(
- currentPage * pageSize,
- filteredDataSource.length,
- );
+ const endIndex = Math.min(currentPage * pageSize, filteredDataSource.length);
const handleBulkSelect = (upName, checked) => {
if (checked) {
@@ -726,22 +710,22 @@ export default function UpstreamRatioSync(props) {
}
onPress={() => {
setModalVisible(true);
if (allChannels.length === 0) fetchAllChannels();
}}
className='w-full md:w-auto'
>
+
{t('选择同步渠道')}
}
isDisabled={!hasSelections}
onPress={applySync}
className='w-full md:w-auto'
>
+
{t('应用同步')}
@@ -754,18 +738,14 @@ export default function UpstreamRatioSync(props) {
- setSearchKeyword(event.target.value)
- }
+ onChange={(event) => setSearchKeyword(event.target.value)}
placeholder={t('搜索模型名称')}
className={`${inputClass} pl-8`}
/>
|
-
- {t('未设置')}
-
+ {t('未设置')}
|
);
}
@@ -966,9 +945,7 @@ export default function UpstreamRatioSync(props) {
{!isConfident && (
- setCurrentPage(Math.max(1, currentPage - 1))
- }
+ onPress={() => setCurrentPage(Math.max(1, currentPage - 1))}
>
{t('上一页')}
@@ -1061,9 +1036,7 @@ export default function UpstreamRatioSync(props) {
setConfirmVisible(false);
const curRatios = {
ModelRatio: JSON.parse(props.options.ModelRatio || '{}'),
- CompletionRatio: JSON.parse(
- props.options.CompletionRatio || '{}',
- ),
+ CompletionRatio: JSON.parse(props.options.CompletionRatio || '{}'),
CacheRatio: JSON.parse(props.options.CacheRatio || '{}'),
ModelPrice: JSON.parse(props.options.ModelPrice || '{}'),
};
diff --git a/web/src/pages/Setting/Ratio/components/AutoGroupList.jsx b/web/src/pages/Setting/Ratio/components/AutoGroupList.jsx
index 84789424aa7..db5117b9e32 100644
--- a/web/src/pages/Setting/Ratio/components/AutoGroupList.jsx
+++ b/web/src/pages/Setting/Ratio/components/AutoGroupList.jsx
@@ -90,12 +90,8 @@ export default function AutoGroupList({ value, groupNames = [], onChange }) {
{t('暂无自动分组,点击下方按钮添加')}
- }
- onPress={addItem}
- size='sm'
- >
+
+
{t('添加分组')}
@@ -161,12 +157,8 @@ export default function AutoGroupList({ value, groupNames = [], onChange }) {
))}
- }
- onPress={addItem}
- size='sm'
- >
+
+
{t('添加分组')}
diff --git a/web/src/pages/Setting/Ratio/components/GroupGroupRatioRules.jsx b/web/src/pages/Setting/Ratio/components/GroupGroupRatioRules.jsx
index d30e51de648..0da9b970d88 100644
--- a/web/src/pages/Setting/Ratio/components/GroupGroupRatioRules.jsx
+++ b/web/src/pages/Setting/Ratio/components/GroupGroupRatioRules.jsx
@@ -1,11 +1,6 @@
import React, { useState, useCallback, useMemo } from 'react';
import { Button, Input } from '@heroui/react';
-import {
- Plus,
- Trash2,
- ChevronDown,
- ChevronUp,
-} from 'lucide-react';
+import { Plus, Trash2, ChevronDown, ChevronUp } from 'lucide-react';
import { useTranslation } from 'react-i18next';
let _idCounter = 0;
@@ -68,14 +63,20 @@ function GroupSection({
const [open, setOpen] = useState(false);
const removeAll = () => {
- if (typeof window !== 'undefined' && !window.confirm(t('确认删除该分组的所有规则?'))) {
+ if (
+ typeof window !== 'undefined' &&
+ !window.confirm(t('确认删除该分组的所有规则?'))
+ ) {
return;
}
items.forEach((item) => onRemove(item._id));
};
const removeOne = (id) => {
- if (typeof window !== 'undefined' && !window.confirm(t('确认删除该规则?'))) {
+ if (
+ typeof window !== 'undefined' &&
+ !window.confirm(t('确认删除该规则?'))
+ ) {
return;
}
onRemove(id);
@@ -269,12 +270,8 @@ export default function GroupGroupRatioRules({
))}
- }
- onPress={addNewGroup}
- size='sm'
- >
+
+
{t('添加分组规则')}
diff --git a/web/src/pages/Setting/Ratio/components/GroupSpecialUsableRules.jsx b/web/src/pages/Setting/Ratio/components/GroupSpecialUsableRules.jsx
index c27dab2bbb8..20b1a1cfc7f 100644
--- a/web/src/pages/Setting/Ratio/components/GroupSpecialUsableRules.jsx
+++ b/web/src/pages/Setting/Ratio/components/GroupSpecialUsableRules.jsx
@@ -18,12 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/
import React, { useState, useCallback, useMemo } from 'react';
import { Button, Input } from '@heroui/react';
-import {
- Plus,
- Trash2,
- ChevronDown,
- ChevronUp,
-} from 'lucide-react';
+import { Plus, Trash2, ChevronDown, ChevronUp } from 'lucide-react';
import { useTranslation } from 'react-i18next';
let _idCounter = 0;
@@ -34,8 +29,10 @@ const OP_REMOVE = 'remove';
const OP_APPEND = 'append';
function parsePrefix(rawKey) {
- if (rawKey.startsWith('+:')) return { op: OP_ADD, groupName: rawKey.slice(2) };
- if (rawKey.startsWith('-:')) return { op: OP_REMOVE, groupName: rawKey.slice(2) };
+ if (rawKey.startsWith('+:'))
+ return { op: OP_ADD, groupName: rawKey.slice(2) };
+ if (rawKey.startsWith('-:'))
+ return { op: OP_REMOVE, groupName: rawKey.slice(2) };
return { op: OP_APPEND, groupName: rawKey };
}
@@ -96,10 +93,8 @@ const inputClass =
const opStyles = {
[OP_ADD]:
'bg-emerald-100 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-300',
- [OP_REMOVE]:
- 'bg-red-100 text-red-700 dark:bg-red-950/40 dark:text-red-300',
- [OP_APPEND]:
- 'bg-sky-100 text-sky-700 dark:bg-sky-950/40 dark:text-sky-300',
+ [OP_REMOVE]: 'bg-red-100 text-red-700 dark:bg-red-950/40 dark:text-red-300',
+ [OP_APPEND]: 'bg-sky-100 text-sky-700 dark:bg-sky-950/40 dark:text-sky-300',
};
function UsableGroupSection({
@@ -114,14 +109,20 @@ function UsableGroupSection({
const [open, setOpen] = useState(false);
const removeAll = () => {
- if (typeof window !== 'undefined' && !window.confirm(t('确认删除该分组的所有规则?'))) {
+ if (
+ typeof window !== 'undefined' &&
+ !window.confirm(t('确认删除该分组的所有规则?'))
+ ) {
return;
}
items.forEach((item) => onRemove(item._id));
};
const removeOne = (id) => {
- if (typeof window !== 'undefined' && !window.confirm(t('确认删除该规则?'))) {
+ if (
+ typeof window !== 'undefined' &&
+ !window.confirm(t('确认删除该规则?'))
+ ) {
return;
}
onRemove(id);
@@ -255,7 +256,8 @@ export default function GroupSpecialUsableRules({
rules.map((r) => {
if (r._id !== id) return r;
const updated = { ...r, [field]: val };
- if (field === 'op' && val === OP_REMOVE) updated.description = 'remove';
+ if (field === 'op' && val === OP_REMOVE)
+ updated.description = 'remove';
else if (field === 'op' && r.op === OP_REMOVE && val !== OP_REMOVE) {
if (updated.description === 'remove') updated.description = '';
}
@@ -351,12 +353,8 @@ export default function GroupSpecialUsableRules({
))}
- }
- onPress={addNewGroup}
- size='sm'
- >
+
+
{t('添加分组规则')}
diff --git a/web/src/pages/Setting/Ratio/components/GroupTable.jsx b/web/src/pages/Setting/Ratio/components/GroupTable.jsx
index 4a84a16788f..17563e30939 100644
--- a/web/src/pages/Setting/Ratio/components/GroupTable.jsx
+++ b/web/src/pages/Setting/Ratio/components/GroupTable.jsx
@@ -107,7 +107,10 @@ export default function GroupTable({ groupRatio, userUsableGroups, onChange }) {
const removeRow = useCallback(
(id) => {
// eslint-disable-next-line no-alert
- if (typeof window !== 'undefined' && !window.confirm(t('确认删除该分组?'))) {
+ if (
+ typeof window !== 'undefined' &&
+ !window.confirm(t('确认删除该分组?'))
+ ) {
return;
}
emitAndSet((prev) => prev.filter((r) => r._id !== id));
@@ -141,7 +144,9 @@ export default function GroupTable({ groupRatio, userUsableGroups, onChange }) {
updateRow(record._id, 'name', event.target.value)}
+ onChange={(event) =>
+ updateRow(record._id, 'name', event.target.value)
+ }
aria-label={t('分组名称')}
className={`${baseInputClass} ${
isDup ? 'border-amber-400 focus:border-amber-500' : ''
@@ -158,7 +163,11 @@ export default function GroupTable({ groupRatio, userUsableGroups, onChange }) {
render: (_, record) => (
{
@@ -244,12 +253,8 @@ export default function GroupTable({ groupRatio, userUsableGroups, onChange }) {
}
/>
- }
- onPress={addRow}
- size='sm'
- >
+
+
{t('添加分组')}
diff --git a/web/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx b/web/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx
index 60a492e3a19..34615f10965 100644
--- a/web/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx
+++ b/web/src/pages/Setting/Ratio/components/ModelPricingEditor.jsx
@@ -88,9 +88,7 @@ function InfoBanner({ tone = 'warning', title, description }) {
}
/>
- {title ? (
- {title}
- ) : null}
+ {title ? {title} : null}
{description ? {description} : null}
@@ -212,15 +210,9 @@ export default function ModelPricingEditor({
}
};
- const totalPages = Math.max(
- 1,
- Math.ceil(filteredModels.length / PAGE_SIZE),
- );
+ const totalPages = Math.max(1, Math.ceil(filteredModels.length / PAGE_SIZE));
const startIndex = (currentPage - 1) * PAGE_SIZE + 1;
- const endIndex = Math.min(
- currentPage * PAGE_SIZE,
- filteredModels.length,
- );
+ const endIndex = Math.min(currentPage * PAGE_SIZE, filteredModels.length);
const allOnPageSelected =
pagedData.length > 0 &&
@@ -276,27 +268,25 @@ export default function ModelPricingEditor({
{allowAddModel ? (
}
onPress={() => setAddVisible(true)}
className={isMobile ? 'w-full' : ''}
>
+
{t('添加模型')}
) : null}
}
onPress={handleSubmit}
className={isMobile ? 'w-full' : ''}
>
+
{t('应用更改')}
setBatchVisible(true)}
className={isMobile ? 'w-full' : ''}
>
@@ -306,10 +296,7 @@ export default function ModelPricingEditor({
: ''}
-
+
- setConflictOnly(event.target.checked)
- }
+ onChange={(event) => setConflictOnly(event.target.checked)}
className='h-4 w-4 accent-primary'
/>
{t('仅显示矛盾倍率')}
@@ -396,8 +381,7 @@ export default function ModelPricingEditor({
const isChecked = selectedModelNames.includes(
record.name,
);
- const isFocused =
- record.name === selectedModelName;
+ const isFocused = record.name === selectedModelName;
const rowBg = isChecked
? 'bg-success/10'
: isFocused
@@ -412,9 +396,7 @@ export default function ModelPricingEditor({
- setSelectedModelName(record.name)
- }
+ onClick={() => setSelectedModelName(record.name)}
>
- toggleRow(
- record.name,
- event.target.checked,
- )
+ toggleRow(record.name, event.target.checked)
}
aria-label={record.name}
className='h-4 w-4 accent-primary'
@@ -530,9 +509,7 @@ export default function ModelPricingEditor({
variant='tertiary'
isDisabled={currentPage >= totalPages}
onPress={() =>
- setCurrentPage(
- Math.min(totalPages, currentPage + 1),
- )
+ setCurrentPage(Math.min(totalPages, currentPage + 1))
}
>
{t('下一页')}
@@ -550,9 +527,7 @@ export default function ModelPricingEditor({
- {selectedModel
- ? selectedModel.name
- : t('模型计费编辑器')}
+ {selectedModel ? selectedModel.name : t('模型计费编辑器')}
{selectedModel ? (
@@ -680,10 +655,7 @@ export default function ModelPricingEditor({
value={selectedModel.completionPrice}
placeholder={t('输入 $/1M tokens')}
onChange={(value) =>
- handleNumericFieldChange(
- 'completionPrice',
- value,
- )
+ handleNumericFieldChange('completionPrice', value)
}
headerAction={
handleOptionalFieldToggle(
'completionPrice',
@@ -885,10 +855,7 @@ export default function ModelPricingEditor({
value={selectedModel.audioInputPrice}
placeholder={t('输入 $/1M tokens')}
onChange={(value) =>
- handleNumericFieldChange(
- 'audioInputPrice',
- value,
- )
+ handleNumericFieldChange('audioInputPrice', value)
}
headerAction={
| | |