Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions web/src/components/common/ErrorBoundary.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import { Empty, Button } from '@douyinfe/semi-ui';
import {
IllustrationFailure,
IllustrationFailureDark,
} from '@douyinfe/semi-illustrations';
import { withTranslation } from 'react-i18next';

class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}

static getDerivedStateFromError() {
return { hasError: true };
}

componentDidCatch(error, errorInfo) {
console.error('[ErrorBoundary]', error, errorInfo);
}

render() {
if (this.state.hasError) {
const { t } = this.props;
return (
<div className='flex flex-col justify-center items-center h-screen p-8'>
<Empty
image={
<IllustrationFailure style={{ width: 250, height: 250 }} />
}
darkModeImage={
<IllustrationFailureDark style={{ width: 250, height: 250 }} />
}
description={t('页面渲染出错,请刷新页面重试')}
/>
<Button
theme='solid'
type='primary'
style={{ marginTop: 16 }}
onClick={() => window.location.reload()}
>
{t('刷新页面')}
</Button>
</div>
);
}
return this.props.children;
}
}

export default withTranslation()(ErrorBoundary);
5 changes: 4 additions & 1 deletion web/src/components/layout/PageLayout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import SiderBar from './SiderBar';
import App from '../../App';
import FooterBar from './Footer';
import { ToastContainer } from 'react-toastify';
import ErrorBoundary from '../common/ErrorBoundary';
import React, { useContext, useEffect, useState } from 'react';
import { useIsMobile } from '../../hooks/common/useIsMobile';
import { useSidebarCollapsed } from '../../hooks/common/useSidebarCollapsed';
Expand Down Expand Up @@ -216,7 +217,9 @@ const PageLayout = () => {
position: 'relative',
}}
>
<App />
<ErrorBoundary>
<App />
</ErrorBoundary>
</Content>
{!shouldHideFooter && (
<Layout.Footer
Expand Down
117 changes: 117 additions & 0 deletions web/src/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -3488,6 +3488,123 @@
"默认测试模型": "Default Test Model",
"默认用户消息": "Default User Message",
"默认补全倍率": "Default completion ratio",
"提示:端点映射仅用于模型广场展示,不会影响模型真实调用。如需配置真实调用,请前往「渠道管理」。": "Notice: Endpoint mapping is for Model Marketplace display only and does not affect real model invocation. To configure real invocation, please go to Channel Management.",
"购买订阅获得模型额度/次数": "Purchase a subscription to get model quota/usage",
"生产环境 RSA 私钥 Base64 (PKCS#8 DER)": "Production RSA private key Base64 (PKCS#8 DER)",
"沙盒环境 RSA 私钥 Base64 (PKCS#8 DER)": "Sandbox RSA private key Base64 (PKCS#8 DER)",
"生产环境 Waffo 公钥 Base64 (X.509 DER)": "Production Waffo public key Base64 (X.509 DER)",
"沙盒环境 Waffo 公钥 Base64 (X.509 DER)": "Sandbox Waffo public key Base64 (X.509 DER)",
"支付方式类型": "Pay Method Type",
"支付方式名称": "Pay Method Name",
"获取充值配置失败": "Failed to get topup configuration",
"获取充值配置异常": "Topup configuration error",
"分组相关设置": "Group Related Settings",
"保存分组相关设置": "Save Group Related Settings",
"此页面仅显示未设置价格或基础倍率的模型,设置后会自动从列表中移出": "This page only shows models without base pricing. After saving, configured models will be removed from this list automatically.",
"没有未设置定价的模型": "No unpriced models",
"当前没有未设置定价的模型": "There are currently no models without pricing",
"模型计费编辑器": "Model Pricing Editor",
"价格摘要": "Price Summary",
"当前提示": "Current Notes",
"这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。": "This editor uses prices by default and converts them back into the ratio JSON required by the backend when saved.",
"当前未启用,需要时再打开即可。": "This field is currently disabled. Enable it when needed.",
"下面展示这个模型保存后会写入哪些后端字段,便于和原始 JSON 编辑框保持一致。": "The fields below show which backend values will be written after saving, so you can keep them aligned with the raw JSON editors.",
"补全价格已锁定": "Completion price is locked",
"后端固定倍率:{{ratio}}。该字段仅展示换算后的价格。": "Backend fixed ratio: {{ratio}}. This field only displays the converted price.",
"这些价格都是可选项,不填也可以。": "All of these prices are optional and can be left empty.",
"请先开启并填写音频输入价格。": "Enable and fill in the audio input price first.",
"输入模型名称,例如 gpt-4.1": "Enter a model name, for example gpt-4.1",
"当前模型同时存在按次价格和倍率配置,保存时会按当前计费方式覆盖。": "This model currently has both per-request pricing and ratio-based pricing. Saving will overwrite them according to the current billing mode.",
"当前模型存在未显式设置输入倍率的扩展倍率;填写输入价格后会自动换算为价格字段。": "This model has derived ratios without an explicit input ratio. Once you fill in the input price, they will be converted into price fields automatically.",
"按量计费下需要先填写输入价格,才能保存其它价格项。": "For per-token billing, fill in the input price before saving other price fields.",
"填写音频补全价格前,需要先填写音频输入价格。": "Fill in the audio input price before setting the audio completion price.",
"模型 {{name}} 缺少输入价格,无法计算补全/缓存/图片/音频价格对应的倍率": "Model {{name}} is missing an input price, so the ratios for completion, cache, image, and audio pricing cannot be calculated.",
"模型 {{name}} 缺少音频输入价格,无法计算音频补全倍率": "Model {{name}} is missing an audio input price, so the audio completion ratio cannot be calculated.",
"批量应用当前模型价格": "Batch Apply Current Model Pricing",
"请先选择一个作为模板的模型": "Please select a model to use as the template first",
"请先勾选需要批量设置的模型": "Please select the models you want to update in batch first",
"已将模型 {{name}} 的价格配置批量应用到 {{count}} 个模型": "Applied the pricing configuration of model {{name}} to {{count}} models in batch",
"将把当前编辑中的模型 {{name}} 的价格配置,批量应用到已勾选的 {{count}} 个模型。": "The pricing configuration of the currently edited model {{name}} will be applied to the {{count}} selected models.",
"适合同系列模型一起定价,例如把 gpt-5.1 的价格批量同步到 gpt-5.1-high、gpt-5.1-low 等模型。": "Useful for pricing model variants together, for example syncing the pricing of gpt-5.1 to gpt-5.1-high, gpt-5.1-low, and similar models.",
"已勾选": "Selected",
"当前编辑": "Editing",
"已勾选 {{count}} 个模型": "{{count}} models selected",
"计费方式": "Billing Mode",
"未设置价格": "Price not set",
"保存预览": "Save Preview",
"基础价格": "Base Pricing",
"扩展价格": "Additional Pricing",
"额外价格项": "Additional price items",
"补全价格": "Completion Price",
"缓存读取价格": "Input Cache Read Price",
"缓存创建价格": "Input Cache Creation Price",
"图片输入价格": "Image Input Price",
"音频输入价格": "Audio Input Price",
"音频输入价格:{{symbol}}{{price}} / 1M tokens": "Audio input price: {{symbol}}{{price}} / 1M tokens",
"音频补全价格": "Audio Completion Price",
"音频补全价格:{{symbol}}{{price}} / 1M tokens": "Audio completion price: {{symbol}}{{price}} / 1M tokens",
"适合 MJ / 任务类等按次收费模型。": "Suitable for MJ and other task-based models billed per request.",
"该模型补全倍率由后端固定为 {{ratio}}。补全价格不能在这里修改。": "This model's completion ratio is fixed to {{ratio}} by the backend. The completion price cannot be changed here.",
"Web 搜索调用 {{webSearchCallCount}} 次": "Web search called {{webSearchCallCount}} times",
"文件搜索调用 {{fileSearchCallCount}} 次": "File search called {{fileSearchCallCount}} times",
"实际结算金额:{{symbol}}{{total}}(已包含分组价格调整)": "Actual charge: {{symbol}}{{total}} (group pricing adjustment included)",
"图片倍率 {{imageRatio}}": "Image ratio {{imageRatio}}",
"音频倍率 {{audioRatio}}": "Audio ratio {{audioRatio}}",
"普通输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Standard input: {{tokens}} / 1M * model ratio {{modelRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"缓存输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cached input: {{tokens}} / 1M * model ratio {{modelRatio}} * cache ratio {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"图片输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 图片倍率 {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Image input: {{tokens}} / 1M * model ratio {{modelRatio}} * image ratio {{imageRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"音频输入:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Audio input: {{tokens}} / 1M * model ratio {{modelRatio}} * audio ratio {{audioRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Output: {{tokens}} / 1M * model ratio {{modelRatio}} * completion ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"Web 搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Web search: {{count}} / 1K * unit price {{price}} * {{ratioType}} {{ratio}} = {{amount}}",
"文件搜索:{{count}} / 1K * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "File search: {{count}} / 1K * unit price {{price}} * {{ratioType}} {{ratio}} = {{amount}}",
"图片生成:1 次 * 单价 {{price}} * {{ratioType}} {{ratio}} = {{amount}}": "Image generation: 1 call * unit price {{price}} * {{ratioType}} {{ratio}} = {{amount}}",
"合计:{{total}}": "Total: {{total}}",
"模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},音频倍率 {{audioRatio}},音频补全倍率 {{audioCompletionRatio}},{{cachePart}}{{ratioType}} {{ratio}}": "Model ratio {{modelRatio}}, completion ratio {{completionRatio}}, audio ratio {{audioRatio}}, audio completion ratio {{audioCompletionRatio}}, {{cachePart}}{{ratioType}} {{ratio}}",
"文字输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 补全倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Text output: {{tokens}} / 1M * model ratio {{modelRatio}} * completion ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"音频输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 音频倍率 {{audioRatio}} * 音频补全倍率 {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Audio output: {{tokens}} / 1M * model ratio {{modelRatio}} * audio ratio {{audioRatio}} * audio completion ratio {{audioCompletionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"合计:文字部分 {{textTotal}} + 音频部分 {{audioTotal}} = {{total}}": "Total: text {{textTotal}} + audio {{audioTotal}} = {{total}}",
"模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},{{ratioType}} {{ratio}}": "Model ratio {{modelRatio}}, output ratio {{completionRatio}}, cache ratio {{cacheRatio}}, {{ratioType}} {{ratio}}",
"缓存读取:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存倍率 {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache read: {{tokens}} / 1M * model ratio {{modelRatio}} * cache ratio {{cacheRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 缓存创建倍率 {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * cache creation ratio {{cacheCreationRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"5m缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 5m缓存创建倍率 {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}": "5m cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 5m cache creation ratio {{cacheCreationRatio5m}} * {{ratioType}} {{ratio}} = {{amount}}",
"1h缓存创建:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 1h缓存创建倍率 {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}": "1h cache creation: {{tokens}} / 1M * model ratio {{modelRatio}} * 1h cache creation ratio {{cacheCreationRatio1h}} * {{ratioType}} {{ratio}} = {{amount}}",
"输出:{{tokens}} / 1M * 模型倍率 {{modelRatio}} * 输出倍率 {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}": "Output: {{tokens}} / 1M * model ratio {{modelRatio}} * output ratio {{completionRatio}} * {{ratioType}} {{ratio}} = {{amount}}",
"空": "Empty",
"{{ratioType}} {{ratio}}x": "{{ratioType}} {{ratio}}x",
"模型价格:{{symbol}}{{price}}": "Model price: {{symbol}}{{price}}",
"模型价格 {{price}}": "Model price {{price}}",
"缓存读 {{price}} / 1M tokens": "Cache read {{price}} / 1M tokens",
"5m缓存创建 {{price}} / 1M tokens": "5m cache creation {{price}} / 1M tokens",
"1h缓存创建 {{price}} / 1M tokens": "1h cache creation {{price}} / 1M tokens",
"缓存创建 {{price}} / 1M tokens": "Cache creation {{price}} / 1M tokens",
"图片输入 {{price}} / 1M tokens": "Image input {{price}} / 1M tokens",
"输入 {{price}} / 1M tokens": "Input {{price}} / 1M tokens",
"缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "Cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
"5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "5m cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
"1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}": "1h cache creation {{tokens}} tokens / 1M tokens * {{symbol}}{{price}}",
"(输入 {{nonImageInput}} tokens + 图片输入 {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}": "(Input {{nonImageInput}} tokens + Image input {{imageInput}} tokens / 1M tokens * {{symbol}}{{price}}",
"图片输入价格:{{symbol}}{{total}} / 1M tokens": "Image input price: {{symbol}}{{total}} / 1M tokens",
"文字提示 {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + 文字补全 {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + 音频提示 {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + 音频补全 {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}": "Text prompt {{input}} tokens / 1M tokens * {{symbol}}{{textInputPrice}} + Text completion {{completion}} tokens / 1M tokens * {{symbol}}{{textCompPrice}} + Audio prompt {{audioInput}} tokens / 1M tokens * {{symbol}}{{audioInputPrice}} + Audio completion {{audioCompletion}} tokens / 1M tokens * {{symbol}}{{audioCompPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}",
"缓存读取价格:{{symbol}}{{total}} / 1M tokens": "Cache read price: {{symbol}}{{total}} / 1M tokens",
"补全 {{completion}} tokens * 输出倍率 {{completionRatio}}": "Completion {{completion}} tokens * Output ratio {{completionRatio}}",
"补全倍率 {{completionRatio}}": "Completion ratio {{completionRatio}}",
"输入价格:{{symbol}}{{price}} / 1M tokens": "Input Price: {{symbol}}{{price}} / 1M tokens",
"输出价格 {{symbol}}{{price}} / 1M tokens": "Output Price {{symbol}}{{price}} / 1M tokens",
"输出价格:{{symbol}}{{price}} / 1M tokens": "Output Price: {{symbol}}{{price}} / 1M tokens",
"输出价格:{{symbol}}{{total}} / 1M tokens": "Output Price: {{symbol}}{{total}} / 1M tokens",
"例如:gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$": "Example: gpt-4.1-nano,regex:^claude-.*$,regex:^sora-.*$",
"支持精确匹配;使用 regex: 开头可按正则匹配。": "Supports exact matching. Use a regex: prefix for regex matching.",
"复制密钥": "Copy Key",
"复制连接信息": "Copy Connection String",
"检测到剪贴板中的连接信息": "Connection info detected in clipboard",
"自动填入": "Auto-fill",
"忽略": "Ignore",
"从剪贴板粘贴配置": "Paste Config",
"剪贴板中未检测到连接信息": "No connection info found in clipboard",
"连接信息已填入": "Connection info applied",
"无法读取剪贴板": "Cannot read clipboard",
"页面渲染出错,请刷新页面重试": "An error occurred while rendering the page. Please refresh and try again.",
"刷新页面": "Reload Page",
"(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)": "(Currently only supports Epay interface, the default callback address is the server address above!)",
",当前无生效订阅,将自动使用钱包": ", no active subscription. Wallet will be used automatically.",
",时间:": ",time:",
Expand Down
Loading