From bc322ddac4bfbebc055bae600ceed156c86357a4 Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Tue, 29 Apr 2025 22:54:43 +0800 Subject: [PATCH 01/18] refactor: optimize Dockerfile apk usage --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 214ceaa3..3b42089b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,8 +24,7 @@ RUN go build -ldflags "-s -w -X 'one-api/common.Version=$(cat VERSION)'" -o one- FROM alpine -RUN apk update \ - && apk upgrade \ +RUN apk upgrade --no-cache \ && apk add --no-cache ca-certificates tzdata ffmpeg \ && update-ca-certificates From 4a313a5f93d09698768f3eeaeee4998147682b6e Mon Sep 17 00:00:00 2001 From: skynono <6811626@qq.com> Date: Thu, 5 Jun 2025 17:31:15 +0800 Subject: [PATCH 02/18] feat: add moonshot(kimi) update balance --- controller/channel-billing.go | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/controller/channel-billing.go b/controller/channel-billing.go index 2bda0fd2..9bf5d1fe 100644 --- a/controller/channel-billing.go +++ b/controller/channel-billing.go @@ -4,11 +4,13 @@ import ( "encoding/json" "errors" "fmt" + "github.com/shopspring/decimal" "io" "net/http" "one-api/common" "one-api/model" "one-api/service" + "one-api/setting" "strconv" "time" @@ -304,6 +306,40 @@ func updateChannelOpenRouterBalance(channel *model.Channel) (float64, error) { return balance, nil } +func updateChannelMoonshotBalance(channel *model.Channel) (float64, error) { + url := "https://api.moonshot.cn/v1/users/me/balance" + body, err := GetResponseBody("GET", url, channel, GetAuthHeader(channel.Key)) + if err != nil { + return 0, err + } + + type MoonshotBalanceData struct { + AvailableBalance float64 `json:"available_balance"` + VoucherBalance float64 `json:"voucher_balance"` + CashBalance float64 `json:"cash_balance"` + } + + type MoonshotBalanceResponse struct { + Code int `json:"code"` + Data MoonshotBalanceData `json:"data"` + Scode string `json:"scode"` + Status bool `json:"status"` + } + + response := MoonshotBalanceResponse{} + err = json.Unmarshal(body, &response) + if err != nil { + return 0, err + } + if !response.Status || response.Code != 0 { + return 0, fmt.Errorf("failed to update moonshot balance, status: %v, code: %d, scode: %s", response.Status, response.Code, response.Scode) + } + availableBalanceCny := response.Data.AvailableBalance + availableBalanceUsd := decimal.NewFromFloat(availableBalanceCny).Div(decimal.NewFromFloat(setting.Price)).InexactFloat64() + channel.UpdateBalance(availableBalanceUsd) + return availableBalanceUsd, nil +} + func updateChannelBalance(channel *model.Channel) (float64, error) { baseURL := common.ChannelBaseURLs[channel.Type] if channel.GetBaseURL() == "" { @@ -332,6 +368,8 @@ func updateChannelBalance(channel *model.Channel) (float64, error) { return updateChannelDeepSeekBalance(channel) case common.ChannelTypeOpenRouter: return updateChannelOpenRouterBalance(channel) + case common.ChannelTypeMoonshot: + return updateChannelMoonshotBalance(channel) default: return 0, errors.New("尚未实现") } From 1ec2bbd533f8f4d95c700c358aff2d613b291bac Mon Sep 17 00:00:00 2001 From: RedwindA Date: Thu, 12 Jun 2025 18:20:58 +0800 Subject: [PATCH 03/18] update input range and description for thinking adapter budget tokens --- web/src/pages/Setting/Model/SettingGeminiModel.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/pages/Setting/Model/SettingGeminiModel.js b/web/src/pages/Setting/Model/SettingGeminiModel.js index b802af1a..6a4d941a 100644 --- a/web/src/pages/Setting/Model/SettingGeminiModel.js +++ b/web/src/pages/Setting/Model/SettingGeminiModel.js @@ -208,8 +208,8 @@ export default function SettingGeminiModel(props) { label={t('请求模型带-thinking后缀的BudgetTokens数(超出24576的部分将被忽略)')} field={'gemini.thinking_adapter_budget_tokens_percentage'} initValue={''} - extraText={t('0.1-1之间的小数')} - min={0.1} + extraText={t('0.002-1之间的小数')} + min={0.002} max={1} onChange={(value) => setInputs({ From a39b2f5aa78de7d4a4e882c1767b661e62489fa0 Mon Sep 17 00:00:00 2001 From: RedwindA Date: Wed, 18 Jun 2025 01:09:09 +0800 Subject: [PATCH 04/18] feat(ratio): add new Gemini model ratios and enhance flash model handling --- setting/operation_setting/model-ratio.go | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/setting/operation_setting/model-ratio.go b/setting/operation_setting/model-ratio.go index 5155b2fc..175995ae 100644 --- a/setting/operation_setting/model-ratio.go +++ b/setting/operation_setting/model-ratio.go @@ -139,6 +139,7 @@ var defaultModelRatio = map[string]float64{ "gemini-2.0-flash": 0.05, "gemini-2.5-pro-exp-03-25": 0.625, "gemini-2.5-pro-preview-03-25": 0.625, + "gemini-2.5-pro": 0.625, "gemini-2.5-flash-preview-04-17": 0.075, "gemini-2.5-flash-preview-04-17-thinking": 0.075, "gemini-2.5-flash-preview-04-17-nothinking": 0.075, @@ -147,6 +148,8 @@ var defaultModelRatio = map[string]float64{ "gemini-2.5-flash-preview-05-20-nothinking": 0.075, "gemini-2.5-flash-thinking-*": 0.075, // 用于为后续所有2.5 flash thinking budget 模型设置默认倍率 "gemini-2.5-pro-thinking-*": 0.625, // 用于为后续所有2.5 pro thinking budget 模型设置默认倍率 + "gemini-2.5-flash-lite-preview-06-17": 0.05, + "gemini-2.5-flash": 0.15, "text-embedding-004": 0.001, "chatglm_turbo": 0.3572, // ¥0.005 / 1k tokens "chatglm_pro": 0.7143, // ¥0.01 / 1k tokens @@ -487,12 +490,17 @@ func getHardcodedCompletionModelRatio(name string) (float64, bool) { return 4, true } else if strings.HasPrefix(name, "gemini-2.5-pro") { // 移除preview来增加兼容性,这里假设正式版的倍率和preview一致 return 8, true - } else if strings.HasPrefix(name, "gemini-2.5-flash") { // 同上 - if strings.HasSuffix(name, "-nothinking") { - return 4, false - } else { - return 3.5 / 0.6, false + } else if strings.HasPrefix(name, "gemini-2.5-flash") { // 处理不同的flash模型倍率 + if strings.HasPrefix(name, "gemini-2.5-flash-preview") { + if strings.HasSuffix(name, "-nothinking") { + return 4, true + } + return 3.5 / 0.15, true } + if strings.HasPrefix(name, "gemini-2.5-flash-lite-preview") { + return 4, true + } + return 2.5 / 0.3, true } return 4, false } From f35784aa97e6f1dee9c140a0a026c3d8a66e0a79 Mon Sep 17 00:00:00 2001 From: RedwindA Date: Wed, 18 Jun 2025 03:25:59 +0800 Subject: [PATCH 05/18] feat(gemini): update audio input pricing and adjust model handling logic --- relay/channel/gemini/relay-gemini.go | 3 +-- setting/operation_setting/tools.go | 9 ++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index 635041d7..d8d7db39 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -103,7 +103,6 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon isNew25Pro := strings.HasPrefix(modelName, "gemini-2.5-pro") && !strings.HasPrefix(modelName, "gemini-2.5-pro-preview-05-06") && !strings.HasPrefix(modelName, "gemini-2.5-pro-preview-03-25") - is25FlashLite := strings.HasPrefix(modelName, "gemini-2.5-flash-lite") if strings.Contains(modelName, "-thinking-") { parts := strings.SplitN(modelName, "-thinking-", 2) @@ -142,7 +141,7 @@ func CovertGemini2OpenAI(textRequest dto.GeneralOpenAIRequest, info *relaycommon } } } else if strings.HasSuffix(modelName, "-nothinking") { - if !isNew25Pro && !is25FlashLite { + if !isNew25Pro { geminiRequest.GenerationConfig.ThinkingConfig = &GeminiThinkingConfig{ ThinkingBudget: common.GetPointer(0), } diff --git a/setting/operation_setting/tools.go b/setting/operation_setting/tools.go index 3e1af99e..daa15e5e 100644 --- a/setting/operation_setting/tools.go +++ b/setting/operation_setting/tools.go @@ -17,6 +17,7 @@ const ( const ( // Gemini Audio Input Price Gemini25FlashPreviewInputAudioPrice = 1.00 + Gemini25FlashProductionInputAudioPrice = 1.00 // for `gemini-2.5-flash` Gemini25FlashNativeAudioInputAudioPrice = 3.00 Gemini20FlashInputAudioPrice = 0.70 ) @@ -64,10 +65,12 @@ func GetFileSearchPricePerThousand() float64 { } func GetGeminiInputAudioPricePerMillionTokens(modelName string) float64 { - if strings.HasPrefix(modelName, "gemini-2.5-flash-preview") { - return Gemini25FlashPreviewInputAudioPrice - } else if strings.HasPrefix(modelName, "gemini-2.5-flash-preview-native-audio") { + if strings.HasPrefix(modelName, "gemini-2.5-flash-preview-native-audio") { return Gemini25FlashNativeAudioInputAudioPrice + } else if strings.HasPrefix(modelName, "gemini-2.5-flash-preview") { + return Gemini25FlashPreviewInputAudioPrice + } else if strings.HasPrefix(modelName, "gemini-2.5-flash") { + return Gemini25FlashProductionInputAudioPrice } else if strings.HasPrefix(modelName, "gemini-2.0-flash") { return Gemini20FlashInputAudioPrice } From b95c5bb8f4fe4f674d4ae11d4c90ec3d9f38c84b Mon Sep 17 00:00:00 2001 From: RedwindA Date: Wed, 18 Jun 2025 03:38:58 +0800 Subject: [PATCH 06/18] feat(gemini): add pricing for Gemini 2.5 Flash Lite preview audio input --- setting/operation_setting/tools.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setting/operation_setting/tools.go b/setting/operation_setting/tools.go index daa15e5e..a401b923 100644 --- a/setting/operation_setting/tools.go +++ b/setting/operation_setting/tools.go @@ -18,6 +18,7 @@ const ( // Gemini Audio Input Price Gemini25FlashPreviewInputAudioPrice = 1.00 Gemini25FlashProductionInputAudioPrice = 1.00 // for `gemini-2.5-flash` + Gemini25FlashLitePreviewInputAudioPrice = 0.50 Gemini25FlashNativeAudioInputAudioPrice = 3.00 Gemini20FlashInputAudioPrice = 0.70 ) @@ -67,6 +68,8 @@ func GetFileSearchPricePerThousand() float64 { func GetGeminiInputAudioPricePerMillionTokens(modelName string) float64 { if strings.HasPrefix(modelName, "gemini-2.5-flash-preview-native-audio") { return Gemini25FlashNativeAudioInputAudioPrice + } else if strings.HasPrefix(modelName, "gemini-2.5-flash-preview-lite") { + return Gemini25FlashLitePreviewInputAudioPrice } else if strings.HasPrefix(modelName, "gemini-2.5-flash-preview") { return Gemini25FlashPreviewInputAudioPrice } else if strings.HasPrefix(modelName, "gemini-2.5-flash") { From c192d07a04acdcb481b95ac8c22c2fe0b27320d3 Mon Sep 17 00:00:00 2001 From: RedwindA Date: Thu, 19 Jun 2025 20:16:04 +0800 Subject: [PATCH 07/18] fix gizmo completion ratio --- setting/ratio_setting/model_ratio.go | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/setting/ratio_setting/model_ratio.go b/setting/ratio_setting/model_ratio.go index 266c1e07..99eba788 100644 --- a/setting/ratio_setting/model_ratio.go +++ b/setting/ratio_setting/model_ratio.go @@ -414,7 +414,12 @@ func UpdateCompletionRatioByJSONString(jsonStr string) error { func GetCompletionRatio(name string) float64 { CompletionRatioMutex.RLock() defer CompletionRatioMutex.RUnlock() - + if strings.HasPrefix(name, "gpt-4-gizmo") { + name = "gpt-4-gizmo-*" + } + if strings.HasPrefix(name, "gpt-4o-gizmo") { + name = "gpt-4o-gizmo-*" + } if strings.Contains(name, "/") { if ratio, ok := CompletionRatio[name]; ok { return ratio @@ -432,12 +437,6 @@ func GetCompletionRatio(name string) float64 { func getHardcodedCompletionModelRatio(name string) (float64, bool) { lowercaseName := strings.ToLower(name) - if strings.HasPrefix(name, "gpt-4-gizmo") { - name = "gpt-4-gizmo-*" - } - if strings.HasPrefix(name, "gpt-4o-gizmo") { - name = "gpt-4o-gizmo-*" - } if strings.HasPrefix(name, "gpt-4") && !strings.HasSuffix(name, "-all") && !strings.HasSuffix(name, "-gizmo-*") { if strings.HasPrefix(name, "gpt-4o") { if name == "gpt-4o-2024-05-13" { From 0b326e7af49d84cd235425f4f0a56d5fbd8d03ee Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Sat, 21 Jun 2025 02:00:58 +0800 Subject: [PATCH 08/18] =?UTF-8?q?=F0=9F=8C=90=20feat(i18n):=20add=20Englis?= =?UTF-8?q?h=20translation=20for=20"=E6=9A=B4=E9=9C=B2=E5=80=8D=E7=8E=87?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add translation "Expose ratio API" for Chinese text "暴露倍率接口" - Update English locale file (en.json) with new translation entry --- web/src/i18n/locales/en.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index ab793364..9a37dd26 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1688,5 +1688,6 @@ "暂无差异化倍率显示": "No differential ratio display", "请先选择同步渠道": "Please select the synchronization channel first", "与本地相同": "Same as local", - "未找到匹配的模型": "No matching model found" + "未找到匹配的模型": "No matching model found", + "暴露倍率接口": "Expose ratio API" } \ No newline at end of file From 9c3a13cb238fa5ea5f5e591a6b219970b5dbfb86 Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Sat, 21 Jun 2025 02:09:08 +0800 Subject: [PATCH 09/18] =?UTF-8?q?=F0=9F=93=9D=20i18n:=20Update=20ratio=20s?= =?UTF-8?q?ync=20message=20text?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Change message from "已与上游倍率完全一致,无需同步" to "未找到差异化倍率,无需同步" - Update English translation to "No differential ratio found, no synchronization is required" - Improve user experience clarity for upstream ratio synchronization status --- web/src/i18n/locales/en.json | 2 +- web/src/pages/Setting/Ratio/UpstreamRatioSync.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 9a37dd26..b89207e5 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1672,7 +1672,7 @@ "获取倍率失败:": "Failed to get ratios: ", "后端请求失败": "Backend request failed", "部分渠道测试失败:": "Some channels failed to test: ", - "已与上游倍率完全一致,无需同步": "The upstream ratio is completely consistent, no synchronization is required", + "未找到差异化倍率,无需同步": "No differential ratio found, no synchronization is required", "请求后端接口失败:": "Failed to request the backend interface: ", "同步成功": "Synchronization successful", "部分保存失败": "Some settings failed to save", diff --git a/web/src/pages/Setting/Ratio/UpstreamRatioSync.js b/web/src/pages/Setting/Ratio/UpstreamRatioSync.js index f83e0cdc..c246a3fe 100644 --- a/web/src/pages/Setting/Ratio/UpstreamRatioSync.js +++ b/web/src/pages/Setting/Ratio/UpstreamRatioSync.js @@ -125,7 +125,7 @@ export default function UpstreamRatioSync(props) { setHasSynced(true); if (Object.keys(differences).length === 0) { - showSuccess(t('已与上游倍率完全一致,无需同步')); + showSuccess(t('未找到差异化倍率,无需同步')); } } catch (e) { showError(t('请求后端接口失败:') + e.message); From edaff1c689bf79ecb415854c191dd7f7bc1b58ff Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sat, 21 Jun 2025 02:21:27 +0800 Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=8E=A8=20feat(UI):=20Add=20Lucide?= =?UTF-8?q?=20icons=20to=20settings=20tabs=20for=20improved=20navigation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add icons to each settings tab to enhance visual recognition - Import necessary Lucide React icons (Settings, Calculator, Gauge, Shapes, etc.) - Create consistent tab styling with icons aligned next to text - Reorder tabs to place "Other Settings" as the last option - Improve overall settings page UI with better visual hierarchy --- web/src/pages/Setting/index.js | 68 ++++++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/web/src/pages/Setting/index.js b/web/src/pages/Setting/index.js index 5572e540..f50586fe 100644 --- a/web/src/pages/Setting/index.js +++ b/web/src/pages/Setting/index.js @@ -2,6 +2,15 @@ import React, { useEffect, useState } from 'react'; import { Layout, TabPane, Tabs } from '@douyinfe/semi-ui'; import { useNavigate, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; +import { + Settings, + Calculator, + Gauge, + Shapes, + Cog, + MoreHorizontal, + LayoutDashboard +} from 'lucide-react'; import SystemSetting from '../../components/settings/SystemSetting.js'; import { isRoot } from '../../helpers'; @@ -21,40 +30,75 @@ const Setting = () => { if (isRoot()) { panes.push({ - tab: t('运营设置'), + tab: ( + + + {t('运营设置')} + + ), content: , itemKey: 'operation', }); panes.push({ - tab: t('倍率设置'), + tab: ( + + + {t('倍率设置')} + + ), content: , itemKey: 'ratio', }); panes.push({ - tab: t('速率限制设置'), + tab: ( + + + {t('速率限制设置')} + + ), content: , itemKey: 'ratelimit', }); panes.push({ - tab: t('模型相关设置'), + tab: ( + + + {t('模型相关设置')} + + ), content: , itemKey: 'models', }); panes.push({ - tab: t('系统设置'), + tab: ( + + + {t('系统设置')} + + ), content: , itemKey: 'system', }); panes.push({ - tab: t('其他设置'), - content: , - itemKey: 'other', - }); - panes.push({ - tab: t('仪表盘设置'), + tab: ( + + + {t('仪表盘设置')} + + ), content: , itemKey: 'dashboard', }); + panes.push({ + tab: ( + + + {t('其他设置')} + + ), + content: , + itemKey: 'other', + }); } const onChangeTab = (key) => { setTabActiveKey(key); @@ -74,7 +118,7 @@ const Setting = () => { onChangeTab(key)} > From 8ec18dd21bcaf966cd3c8e305715509ae30e2b9d Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Sat, 21 Jun 2025 02:36:09 +0800 Subject: [PATCH 11/18] =?UTF-8?q?=F0=9F=92=AC=20refactor:=20separate=20cha?= =?UTF-8?q?t=20settings=20into=20dedicated=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create new ChatsSetting component for managing chat configurations - Add "Chat Settings" tab with MessageSquare icon in settings page - Remove chat settings section from OperationSetting component - Update import path to use Chat directory structure --- web/src/components/settings/ChatsSetting.js | 63 +++++++++++++++++++ .../components/settings/OperationSetting.js | 8 --- .../components/settings/RateLimitSetting.js | 19 +++--- web/src/i18n/locales/en.json | 2 +- .../{Operation => Chat}/SettingsChats.js | 27 +------- .../Setting/Operation/SettingsGeneral.js | 5 -- web/src/pages/Setting/index.js | 14 ++++- 7 files changed, 87 insertions(+), 51 deletions(-) create mode 100644 web/src/components/settings/ChatsSetting.js rename web/src/pages/Setting/{Operation => Chat}/SettingsChats.js (85%) diff --git a/web/src/components/settings/ChatsSetting.js b/web/src/components/settings/ChatsSetting.js new file mode 100644 index 00000000..6330808d --- /dev/null +++ b/web/src/components/settings/ChatsSetting.js @@ -0,0 +1,63 @@ +import React, { useEffect, useState } from 'react'; +import { Card, Spin } from '@douyinfe/semi-ui'; +import SettingsChats from '../../pages/Setting/Chat/SettingsChats.js'; +import { API, showError } from '../../helpers'; + +const ChatsSetting = () => { + let [inputs, setInputs] = useState({ + /* 聊天设置 */ + Chats: '[]', + }); + + let [loading, setLoading] = useState(false); + + const getOptions = async () => { + const res = await API.get('/api/option/'); + const { success, message, data } = res.data; + if (success) { + let newInputs = {}; + data.forEach((item) => { + if ( + item.key.endsWith('Enabled') || + ['DefaultCollapseSidebar'].includes(item.key) + ) { + newInputs[item.key] = item.value === 'true' ? true : false; + } else { + newInputs[item.key] = item.value; + } + }); + + setInputs(newInputs); + } else { + showError(message); + } + }; + + async function onRefresh() { + try { + setLoading(true); + await getOptions(); + } catch (error) { + showError('刷新失败'); + } finally { + setLoading(false); + } + } + + useEffect(() => { + onRefresh(); + }, []); + + return ( + <> + + {/* 聊天设置 */} + + + + + + ); +}; + +export default ChatsSetting; \ No newline at end of file diff --git a/web/src/components/settings/OperationSetting.js b/web/src/components/settings/OperationSetting.js index f6786f95..779d80a6 100644 --- a/web/src/components/settings/OperationSetting.js +++ b/web/src/components/settings/OperationSetting.js @@ -7,7 +7,6 @@ import SettingsLog from '../../pages/Setting/Operation/SettingsLog.js'; import SettingsDataDashboard from '../../pages/Setting/Operation/SettingsDataDashboard.js'; import SettingsMonitoring from '../../pages/Setting/Operation/SettingsMonitoring.js'; import SettingsCreditLimit from '../../pages/Setting/Operation/SettingsCreditLimit.js'; -import SettingsChats from '../../pages/Setting/Operation/SettingsChats.js'; import { API, showError } from '../../helpers'; const OperationSetting = () => { @@ -56,9 +55,6 @@ const OperationSetting = () => { AutomaticDisableChannelEnabled: false, AutomaticEnableChannelEnabled: false, AutomaticDisableKeywords: '', - - /* 聊天设置 */ - Chats: '[]', }); let [loading, setLoading] = useState(false); @@ -131,10 +127,6 @@ const OperationSetting = () => { - {/* 聊天设置 */} - - - ); diff --git a/web/src/components/settings/RateLimitSetting.js b/web/src/components/settings/RateLimitSetting.js index 4d061363..21580d1a 100644 --- a/web/src/components/settings/RateLimitSetting.js +++ b/web/src/components/settings/RateLimitSetting.js @@ -1,8 +1,7 @@ import React, { useEffect, useState } from 'react'; -import { Card, Spin, Tabs } from '@douyinfe/semi-ui'; +import { Card, Spin } from '@douyinfe/semi-ui'; -import { API, showError, showSuccess } from '../../helpers/index.js'; -import SettingsChats from '../../pages/Setting/Operation/SettingsChats.js'; +import { API, showError } from '../../helpers/index.js'; import { useTranslation } from 'react-i18next'; import RequestRateLimit from '../../pages/Setting/RateLimit/SettingsRequestRateLimit.js'; @@ -24,14 +23,14 @@ const RateLimitSetting = () => { if (success) { let newInputs = {}; data.forEach((item) => { - if (item.key === 'ModelRequestRateLimitGroup') { - item.value = JSON.stringify(JSON.parse(item.value), null, 2); - } + if (item.key === 'ModelRequestRateLimitGroup') { + item.value = JSON.stringify(JSON.parse(item.value), null, 2); + } - if (item.key.endsWith('Enabled')) { - newInputs[item.key] = item.value === 'true' ? true : false; - } else { - newInputs[item.key] = item.value; + if (item.key.endsWith('Enabled')) { + newInputs[item.key] = item.value === 'true' ? true : false; + } else { + newInputs[item.key] = item.value; } }); diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index b89207e5..d9d5486e 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1206,7 +1206,7 @@ "默认折叠侧边栏": "Default collapse sidebar", "聊天链接功能已经弃用,请使用下方聊天设置功能": "Chat link function has been deprecated, please use the chat settings below", "你似乎并没有修改什么": "You seem to have not modified anything", - "令牌聊天设置": "Chat settings", + "聊天设置": "Chat settings", "必须将上方聊天链接全部设置为空,才能使用下方聊天设置功能": "Must set all chat links above to empty to use the chat settings below", "链接中的{key}将自动替换为sk-xxxx,{address}将自动替换为系统设置的服务器地址,末尾不带/和/v1": "The {key} in the link will be automatically replaced with sk-xxxx, the {address} will be automatically replaced with the server address in system settings, and the end will not have / and /v1", "聊天配置": "Chat configuration", diff --git a/web/src/pages/Setting/Operation/SettingsChats.js b/web/src/pages/Setting/Chat/SettingsChats.js similarity index 85% rename from web/src/pages/Setting/Operation/SettingsChats.js rename to web/src/pages/Setting/Chat/SettingsChats.js index 4322f558..76f3f9f2 100644 --- a/web/src/pages/Setting/Operation/SettingsChats.js +++ b/web/src/pages/Setting/Chat/SettingsChats.js @@ -2,10 +2,7 @@ import React, { useEffect, useState, useRef } from 'react'; import { Banner, Button, - Col, Form, - Popconfirm, - Row, Space, Spin, } from '@douyinfe/semi-ui'; @@ -16,7 +13,6 @@ import { showSuccess, showWarning, verifyJSON, - verifyJSONPromise, } from '../../../helpers'; import { useTranslation } from 'react-i18next'; @@ -80,21 +76,6 @@ export default function SettingsChats(props) { } } - async function resetModelRatio() { - try { - let res = await API.post(`/api/option/rest_model_ratio`); - // return {success, message} - if (res.data.success) { - showSuccess(res.data.message); - props.refresh(); - } else { - showError(res.data.message); - } - } catch (error) { - showError(error); - } - } - useEffect(() => { const currentInputs = {}; for (let key in props.options) { @@ -119,13 +100,7 @@ export default function SettingsChats(props) { getFormApi={(formAPI) => (refForm.current = formAPI)} style={{ marginBottom: 15 }} > - - + -
(refForm.current = formAPI)} diff --git a/web/src/pages/Setting/index.js b/web/src/pages/Setting/index.js index f50586fe..591bfb34 100644 --- a/web/src/pages/Setting/index.js +++ b/web/src/pages/Setting/index.js @@ -9,7 +9,8 @@ import { Shapes, Cog, MoreHorizontal, - LayoutDashboard + LayoutDashboard, + MessageSquare } from 'lucide-react'; import SystemSetting from '../../components/settings/SystemSetting.js'; @@ -20,6 +21,7 @@ import RateLimitSetting from '../../components/settings/RateLimitSetting.js'; import ModelSetting from '../../components/settings/ModelSetting.js'; import DashboardSetting from '../../components/settings/DashboardSetting.js'; import RatioSetting from '../../components/settings/RatioSetting.js'; +import ChatsSetting from '../../components/settings/ChatsSetting.js'; const Setting = () => { const { t } = useTranslation(); @@ -39,6 +41,16 @@ const Setting = () => { content: , itemKey: 'operation', }); + panes.push({ + tab: ( + + + {t('聊天设置')} + + ), + content: , + itemKey: 'chats', + }); panes.push({ tab: ( From 18417bacb37d9e872bf470992a6dd890609ee5eb Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Sat, 21 Jun 2025 02:50:09 +0800 Subject: [PATCH 12/18] =?UTF-8?q?=F0=9F=8E=A8=20refactor(UI):=20Move=20dra?= =?UTF-8?q?wing=20settings=20to=20a=20separate=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create a new DrawingSetting component for managing drawing-related configurations - Add a dedicated "Drawing Settings" tab with Palette icon in the settings page - Remove drawing settings section from the OperationSetting component - Update import path to use Drawing directory instead of Operation directory - Improve UI organization by separating drawing settings from general operations --- web/src/components/settings/DrawingSetting.js | 65 +++++++++++++++++++ .../components/settings/OperationSetting.js | 13 ---- .../{Operation => Drawing}/SettingsDrawing.js | 0 web/src/pages/Setting/index.js | 15 ++++- 4 files changed, 79 insertions(+), 14 deletions(-) create mode 100644 web/src/components/settings/DrawingSetting.js rename web/src/pages/Setting/{Operation => Drawing}/SettingsDrawing.js (100%) diff --git a/web/src/components/settings/DrawingSetting.js b/web/src/components/settings/DrawingSetting.js new file mode 100644 index 00000000..d2cdce1e --- /dev/null +++ b/web/src/components/settings/DrawingSetting.js @@ -0,0 +1,65 @@ +import React, { useEffect, useState } from 'react'; +import { Card, Spin } from '@douyinfe/semi-ui'; +import SettingsDrawing from '../../pages/Setting/Drawing/SettingsDrawing.js'; +import { API, showError } from '../../helpers'; + +const DrawingSetting = () => { + let [inputs, setInputs] = useState({ + /* 绘图设置 */ + DrawingEnabled: false, + MjNotifyEnabled: false, + MjAccountFilterEnabled: false, + MjForwardUrlEnabled: false, + MjModeClearEnabled: false, + MjActionCheckSuccessEnabled: false, + }); + + let [loading, setLoading] = useState(false); + + const getOptions = async () => { + const res = await API.get('/api/option/'); + const { success, message, data } = res.data; + if (success) { + let newInputs = {}; + data.forEach((item) => { + if (item.key.endsWith('Enabled')) { + newInputs[item.key] = item.value === 'true' ? true : false; + } else { + newInputs[item.key] = item.value; + } + }); + + setInputs(newInputs); + } else { + showError(message); + } + }; + + async function onRefresh() { + try { + setLoading(true); + await getOptions(); + } catch (error) { + showError('刷新失败'); + } finally { + setLoading(false); + } + } + + useEffect(() => { + onRefresh(); + }, []); + + return ( + <> + + {/* 绘图设置 */} + + + + + + ); +}; + +export default DrawingSetting; \ No newline at end of file diff --git a/web/src/components/settings/OperationSetting.js b/web/src/components/settings/OperationSetting.js index 779d80a6..9b0e772c 100644 --- a/web/src/components/settings/OperationSetting.js +++ b/web/src/components/settings/OperationSetting.js @@ -1,7 +1,6 @@ import React, { useEffect, useState } from 'react'; import { Card, Spin } from '@douyinfe/semi-ui'; import SettingsGeneral from '../../pages/Setting/Operation/SettingsGeneral.js'; -import SettingsDrawing from '../../pages/Setting/Operation/SettingsDrawing.js'; import SettingsSensitiveWords from '../../pages/Setting/Operation/SettingsSensitiveWords.js'; import SettingsLog from '../../pages/Setting/Operation/SettingsLog.js'; import SettingsDataDashboard from '../../pages/Setting/Operation/SettingsDataDashboard.js'; @@ -28,14 +27,6 @@ const OperationSetting = () => { DemoSiteEnabled: false, SelfUseModeEnabled: false, - /* 绘图设置 */ - DrawingEnabled: false, - MjNotifyEnabled: false, - MjAccountFilterEnabled: false, - MjForwardUrlEnabled: false, - MjModeClearEnabled: false, - MjActionCheckSuccessEnabled: false, - /* 敏感词设置 */ CheckSensitiveEnabled: false, CheckSensitiveOnPromptEnabled: false, @@ -103,10 +94,6 @@ const OperationSetting = () => { - {/* 绘图设置 */} - - - {/* 屏蔽词过滤设置 */} diff --git a/web/src/pages/Setting/Operation/SettingsDrawing.js b/web/src/pages/Setting/Drawing/SettingsDrawing.js similarity index 100% rename from web/src/pages/Setting/Operation/SettingsDrawing.js rename to web/src/pages/Setting/Drawing/SettingsDrawing.js diff --git a/web/src/pages/Setting/index.js b/web/src/pages/Setting/index.js index 591bfb34..09b9acdf 100644 --- a/web/src/pages/Setting/index.js +++ b/web/src/pages/Setting/index.js @@ -10,7 +10,8 @@ import { Cog, MoreHorizontal, LayoutDashboard, - MessageSquare + MessageSquare, + Palette } from 'lucide-react'; import SystemSetting from '../../components/settings/SystemSetting.js'; @@ -22,6 +23,7 @@ import ModelSetting from '../../components/settings/ModelSetting.js'; import DashboardSetting from '../../components/settings/DashboardSetting.js'; import RatioSetting from '../../components/settings/RatioSetting.js'; import ChatsSetting from '../../components/settings/ChatsSetting.js'; +import DrawingSetting from '../../components/settings/DrawingSetting.js'; const Setting = () => { const { t } = useTranslation(); @@ -51,6 +53,16 @@ const Setting = () => { content: , itemKey: 'chats', }); + panes.push({ + tab: ( + + + {t('绘图设置')} + + ), + content: , + itemKey: 'drawing', + }); panes.push({ tab: ( @@ -131,6 +143,7 @@ const Setting = () => { onChangeTab(key)} > From c3d2d07b6853a9f5e18fb87771cf16f513407c06 Mon Sep 17 00:00:00 2001 From: "Apple\\Apple" Date: Sat, 21 Jun 2025 02:56:38 +0800 Subject: [PATCH 13/18] =?UTF-8?q?=F0=9F=9A=9A=20refactor:=20Move=20DataDas?= =?UTF-8?q?hboard=20settings=20from=20Operation=20to=20Dashboard=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit relocates the DataDashboard settings component from the Operation section to the Dashboard section for better logical organization. The changes include: - Remove DataDashboard import and component from OperationSetting.js - Add DataDashboard component to DashboardSetting.js - Update import path from Operation to Dashboard directory - Add DataExport related state management in DashboardSetting This restructuring improves the application's information architecture by grouping related dashboard visualization settings together. --- web/src/components/settings/DashboardSetting.js | 15 +++++++++++++++ web/src/components/settings/OperationSetting.js | 10 ---------- .../SettingsDataDashboard.js | 0 3 files changed, 15 insertions(+), 10 deletions(-) rename web/src/pages/Setting/{Operation => Dashboard}/SettingsDataDashboard.js (100%) diff --git a/web/src/components/settings/DashboardSetting.js b/web/src/components/settings/DashboardSetting.js index bf4a26a3..86af88c0 100644 --- a/web/src/components/settings/DashboardSetting.js +++ b/web/src/components/settings/DashboardSetting.js @@ -5,6 +5,7 @@ import SettingsAPIInfo from '../../pages/Setting/Dashboard/SettingsAPIInfo.js'; import SettingsAnnouncements from '../../pages/Setting/Dashboard/SettingsAnnouncements.js'; import SettingsFAQ from '../../pages/Setting/Dashboard/SettingsFAQ.js'; import SettingsUptimeKuma from '../../pages/Setting/Dashboard/SettingsUptimeKuma.js'; +import SettingsDataDashboard from '../../pages/Setting/Dashboard/SettingsDataDashboard.js'; const DashboardSetting = () => { let [inputs, setInputs] = useState({ @@ -23,6 +24,11 @@ const DashboardSetting = () => { FAQ: '', UptimeKumaUrl: '', UptimeKumaSlug: '', + + /* 数据看板 */ + DataExportEnabled: false, + DataExportDefaultTime: 'hour', + DataExportInterval: 5, }); let [loading, setLoading] = useState(false); @@ -37,6 +43,10 @@ const DashboardSetting = () => { if (item.key in inputs) { newInputs[item.key] = item.value; } + if (item.key.endsWith('Enabled') && + (item.key === 'DataExportEnabled')) { + newInputs[item.key] = item.value === 'true' ? true : false; + } }); setInputs(newInputs); } else { @@ -106,6 +116,11 @@ const DashboardSetting = () => {

+ {/* 数据看板设置 */} + + + + {/* API信息管理 */} diff --git a/web/src/components/settings/OperationSetting.js b/web/src/components/settings/OperationSetting.js index 9b0e772c..75a5c81a 100644 --- a/web/src/components/settings/OperationSetting.js +++ b/web/src/components/settings/OperationSetting.js @@ -3,7 +3,6 @@ import { Card, Spin } from '@douyinfe/semi-ui'; import SettingsGeneral from '../../pages/Setting/Operation/SettingsGeneral.js'; import SettingsSensitiveWords from '../../pages/Setting/Operation/SettingsSensitiveWords.js'; import SettingsLog from '../../pages/Setting/Operation/SettingsLog.js'; -import SettingsDataDashboard from '../../pages/Setting/Operation/SettingsDataDashboard.js'; import SettingsMonitoring from '../../pages/Setting/Operation/SettingsMonitoring.js'; import SettingsCreditLimit from '../../pages/Setting/Operation/SettingsCreditLimit.js'; import { API, showError } from '../../helpers'; @@ -35,11 +34,6 @@ const OperationSetting = () => { /* 日志设置 */ LogConsumeEnabled: false, - /* 数据看板 */ - DataExportEnabled: false, - DataExportDefaultTime: 'hour', - DataExportInterval: 5, - /* 监控设置 */ ChannelDisableThreshold: 0, QuotaRemindThreshold: 0, @@ -102,10 +96,6 @@ const OperationSetting = () => { - {/* 数据看板 */} - - - {/* 监控设置 */} diff --git a/web/src/pages/Setting/Operation/SettingsDataDashboard.js b/web/src/pages/Setting/Dashboard/SettingsDataDashboard.js similarity index 100% rename from web/src/pages/Setting/Operation/SettingsDataDashboard.js rename to web/src/pages/Setting/Dashboard/SettingsDataDashboard.js From 01ef1fe4e4984802ef344ac1dbed49cf66476a6f Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sat, 21 Jun 2025 04:16:01 +0800 Subject: [PATCH 14/18] =?UTF-8?q?=F0=9F=93=9D=20refactor:=20reorganize=20p?= =?UTF-8?q?ayment=20settings=20into=20dedicated=20tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restructure payment settings into a separate tab for better organization and user experience. The changes include: 1. Create dedicated Payment components in the Setting directory structure 2. Move payment-related settings from SystemSetting to PaymentSetting 3. Add proper i18n support with useTranslation hook 4. Split payment settings into GeneralPayment and PaymentGateway components 5. Fix internationalization issues in placeholder text 6. Update navigation with CreditCard icon for payment tab This refactoring improves code maintainability by following the established project pattern of having specialized setting components in their own directories. --- web/src/components/settings/PaymentSetting.js | 88 +++++++ web/src/components/settings/SystemSetting.js | 150 ------------ web/src/i18n/locales/en.json | 13 +- .../Setting/Payment/SettingsGeneralPayment.js | 74 ++++++ .../Setting/Payment/SettingsPaymentGateway.js | 218 ++++++++++++++++++ web/src/pages/Setting/index.js | 14 +- 6 files changed, 405 insertions(+), 152 deletions(-) create mode 100644 web/src/components/settings/PaymentSetting.js create mode 100644 web/src/pages/Setting/Payment/SettingsGeneralPayment.js create mode 100644 web/src/pages/Setting/Payment/SettingsPaymentGateway.js diff --git a/web/src/components/settings/PaymentSetting.js b/web/src/components/settings/PaymentSetting.js new file mode 100644 index 00000000..91a40a2b --- /dev/null +++ b/web/src/components/settings/PaymentSetting.js @@ -0,0 +1,88 @@ +import React, { useEffect, useState } from 'react'; +import { Card, Spin } from '@douyinfe/semi-ui'; +import SettingsGeneralPayment from '../../pages/Setting/Payment/SettingsGeneralPayment.js'; +import SettingsPaymentGateway from '../../pages/Setting/Payment/SettingsPaymentGateway.js'; +import { API, showError } from '../../helpers'; +import { useTranslation } from 'react-i18next'; + +const PaymentSetting = () => { + const { t } = useTranslation(); + let [inputs, setInputs] = useState({ + ServerAddress: '', + PayAddress: '', + EpayId: '', + EpayKey: '', + Price: 7.3, + MinTopUp: 1, + TopupGroupRatio: '', + CustomCallbackAddress: '', + PayMethods: '', + }); + + let [loading, setLoading] = useState(false); + + const getOptions = async () => { + const res = await API.get('/api/option/'); + const { success, message, data } = res.data; + if (success) { + let newInputs = {}; + data.forEach((item) => { + switch (item.key) { + case 'TopupGroupRatio': + try { + newInputs[item.key] = JSON.stringify(JSON.parse(item.value), null, 2); + } catch (error) { + console.error('解析TopupGroupRatio出错:', error); + newInputs[item.key] = item.value; + } + break; + case 'Price': + case 'MinTopUp': + newInputs[item.key] = parseFloat(item.value); + break; + default: + if (item.key.endsWith('Enabled')) { + newInputs[item.key] = item.value === 'true' ? true : false; + } else { + newInputs[item.key] = item.value; + } + break; + } + }); + + setInputs(newInputs); + } else { + showError(t(message)); + } + }; + + async function onRefresh() { + try { + setLoading(true); + await getOptions(); + } catch (error) { + showError(t('刷新失败')); + } finally { + setLoading(false); + } + } + + useEffect(() => { + onRefresh(); + }, []); + + return ( + <> + + + + + + + + + + ); +}; + +export default PaymentSetting; \ No newline at end of file diff --git a/web/src/components/settings/SystemSetting.js b/web/src/components/settings/SystemSetting.js index 1236ef2e..b5829f31 100644 --- a/web/src/components/settings/SystemSetting.js +++ b/web/src/components/settings/SystemSetting.js @@ -17,7 +17,6 @@ import { removeTrailingSlash, showError, showSuccess, - verifyJSON, } from '../../helpers'; import axios from 'axios'; @@ -42,17 +41,9 @@ const SystemSetting = () => { SMTPAccount: '', SMTPFrom: '', SMTPToken: '', - ServerAddress: '', WorkerUrl: '', WorkerValidKey: '', WorkerAllowHttpImageRequestEnabled: '', - EpayId: '', - EpayKey: '', - Price: 7.3, - MinTopUp: 1, - TopupGroupRatio: '', - PayAddress: '', - CustomCallbackAddress: '', Footer: '', WeChatAuthEnabled: '', WeChatServerAddress: '', @@ -73,7 +64,6 @@ const SystemSetting = () => { LinuxDOOAuthEnabled: '', LinuxDOClientId: '', LinuxDOClientSecret: '', - PayMethods: '', }); const [originInputs, setOriginInputs] = useState({}); @@ -200,11 +190,6 @@ const SystemSetting = () => { setInputs(values); }; - const submitServerAddress = async () => { - let ServerAddress = removeTrailingSlash(inputs.ServerAddress); - await updateOptions([{ key: 'ServerAddress', value: ServerAddress }]); - }; - const submitWorker = async () => { let WorkerUrl = removeTrailingSlash(inputs.WorkerUrl); const options = [ @@ -220,56 +205,6 @@ const SystemSetting = () => { await updateOptions(options); }; - const submitPayAddress = async () => { - if (inputs.ServerAddress === '') { - showError('请先填写服务器地址'); - return; - } - if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { - if (!verifyJSON(inputs.TopupGroupRatio)) { - showError('充值分组倍率不是合法的 JSON 字符串'); - return; - } - } - if (originInputs['PayMethods'] !== inputs.PayMethods) { - if (!verifyJSON(inputs.PayMethods)) { - showError('充值方式设置不是合法的 JSON 字符串'); - return; - } - } - - const options = [ - { key: 'PayAddress', value: removeTrailingSlash(inputs.PayAddress) }, - ]; - - if (inputs.EpayId !== '') { - options.push({ key: 'EpayId', value: inputs.EpayId }); - } - if (inputs.EpayKey !== undefined && inputs.EpayKey !== '') { - options.push({ key: 'EpayKey', value: inputs.EpayKey }); - } - if (inputs.Price !== '') { - options.push({ key: 'Price', value: inputs.Price.toString() }); - } - if (inputs.MinTopUp !== '') { - options.push({ key: 'MinTopUp', value: inputs.MinTopUp.toString() }); - } - if (inputs.CustomCallbackAddress !== '') { - options.push({ - key: 'CustomCallbackAddress', - value: inputs.CustomCallbackAddress, - }); - } - if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { - options.push({ key: 'TopupGroupRatio', value: inputs.TopupGroupRatio }); - } - if (originInputs['PayMethods'] !== inputs.PayMethods) { - options.push({ key: 'PayMethods', value: inputs.PayMethods }); - } - - await updateOptions(options); - }; - const submitSMTP = async () => { const options = []; @@ -551,17 +486,6 @@ const SystemSetting = () => { marginTop: '10px', }} > - - - - - - @@ -604,80 +528,6 @@ const SystemSetting = () => { - - - - (当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { + if (props.options && formApiRef.current) { + const currentInputs = { ServerAddress: props.options.ServerAddress || '' }; + setInputs(currentInputs); + formApiRef.current.setValues(currentInputs); + } + }, [props.options]); + + const handleFormChange = (values) => { + setInputs(values); + }; + + const submitServerAddress = async () => { + setLoading(true); + try { + let ServerAddress = removeTrailingSlash(inputs.ServerAddress); + const res = await API.put('/api/option/', { + key: 'ServerAddress', + value: ServerAddress, + }); + if (res.data.success) { + showSuccess(t('更新成功')); + props.refresh && props.refresh(); + } else { + showError(res.data.message); + } + } catch (error) { + showError(t('更新失败')); + } + setLoading(false); + }; + + return ( + + (formApiRef.current = api)} + > + + + + + + + ); +} \ No newline at end of file diff --git a/web/src/pages/Setting/Payment/SettingsPaymentGateway.js b/web/src/pages/Setting/Payment/SettingsPaymentGateway.js new file mode 100644 index 00000000..0bb63b53 --- /dev/null +++ b/web/src/pages/Setting/Payment/SettingsPaymentGateway.js @@ -0,0 +1,218 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { + Button, + Form, + Row, + Col, + Typography, + Spin, +} from '@douyinfe/semi-ui'; +const { Text } = Typography; +import { + API, + removeTrailingSlash, + showError, + showSuccess, + verifyJSON, +} from '../../../helpers'; +import { useTranslation } from 'react-i18next'; + +export default function SettingsPaymentGateway(props) { + const { t } = useTranslation(); + const [loading, setLoading] = useState(false); + const [inputs, setInputs] = useState({ + PayAddress: '', + EpayId: '', + EpayKey: '', + Price: 7.3, + MinTopUp: 1, + TopupGroupRatio: '', + CustomCallbackAddress: '', + PayMethods: '', + }); + const [originInputs, setOriginInputs] = useState({}); + const formApiRef = useRef(null); + + useEffect(() => { + if (props.options && formApiRef.current) { + const currentInputs = { + PayAddress: props.options.PayAddress || '', + EpayId: props.options.EpayId || '', + EpayKey: props.options.EpayKey || '', + Price: props.options.Price !== undefined ? parseFloat(props.options.Price) : 7.3, + MinTopUp: props.options.MinTopUp !== undefined ? parseFloat(props.options.MinTopUp) : 1, + TopupGroupRatio: props.options.TopupGroupRatio || '', + CustomCallbackAddress: props.options.CustomCallbackAddress || '', + PayMethods: props.options.PayMethods || '', + }; + setInputs(currentInputs); + setOriginInputs({ ...currentInputs }); + formApiRef.current.setValues(currentInputs); + } + }, [props.options]); + + const handleFormChange = (values) => { + setInputs(values); + }; + + const submitPayAddress = async () => { + if (props.options.ServerAddress === '') { + showError(t('请先填写服务器地址')); + return; + } + + if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { + if (!verifyJSON(inputs.TopupGroupRatio)) { + showError(t('充值分组倍率不是合法的 JSON 字符串')); + return; + } + } + + if (originInputs['PayMethods'] !== inputs.PayMethods) { + if (!verifyJSON(inputs.PayMethods)) { + showError(t('充值方式设置不是合法的 JSON 字符串')); + return; + } + } + + setLoading(true); + try { + const options = [ + { key: 'PayAddress', value: removeTrailingSlash(inputs.PayAddress) }, + ]; + + if (inputs.EpayId !== '') { + options.push({ key: 'EpayId', value: inputs.EpayId }); + } + if (inputs.EpayKey !== undefined && inputs.EpayKey !== '') { + options.push({ key: 'EpayKey', value: inputs.EpayKey }); + } + if (inputs.Price !== '') { + options.push({ key: 'Price', value: inputs.Price.toString() }); + } + if (inputs.MinTopUp !== '') { + options.push({ key: 'MinTopUp', value: inputs.MinTopUp.toString() }); + } + if (inputs.CustomCallbackAddress !== '') { + options.push({ + key: 'CustomCallbackAddress', + value: inputs.CustomCallbackAddress, + }); + } + if (originInputs['TopupGroupRatio'] !== inputs.TopupGroupRatio) { + options.push({ key: 'TopupGroupRatio', value: inputs.TopupGroupRatio }); + } + if (originInputs['PayMethods'] !== inputs.PayMethods) { + options.push({ key: 'PayMethods', value: inputs.PayMethods }); + } + + // 发送请求 + const requestQueue = options.map(opt => + API.put('/api/option/', { + key: opt.key, + value: opt.value, + }) + ); + + const results = await Promise.all(requestQueue); + + // 检查所有请求是否成功 + const errorResults = results.filter(res => !res.data.success); + if (errorResults.length > 0) { + errorResults.forEach(res => { + showError(res.data.message); + }); + } else { + showSuccess(t('更新成功')); + // 更新本地存储的原始值 + setOriginInputs({ ...inputs }); + props.refresh && props.refresh(); + } + } catch (error) { + showError(t('更新失败')); + } + setLoading(false); + }; + + return ( + +
(formApiRef.current = api)} + > + + + {t('(当前仅支持易支付接口,默认使用上方服务器地址作为回调地址!)')} + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ ); +} \ No newline at end of file diff --git a/web/src/pages/Setting/index.js b/web/src/pages/Setting/index.js index 09b9acdf..d0ae763b 100644 --- a/web/src/pages/Setting/index.js +++ b/web/src/pages/Setting/index.js @@ -11,7 +11,8 @@ import { MoreHorizontal, LayoutDashboard, MessageSquare, - Palette + Palette, + CreditCard } from 'lucide-react'; import SystemSetting from '../../components/settings/SystemSetting.js'; @@ -24,6 +25,7 @@ import DashboardSetting from '../../components/settings/DashboardSetting.js'; import RatioSetting from '../../components/settings/RatioSetting.js'; import ChatsSetting from '../../components/settings/ChatsSetting.js'; import DrawingSetting from '../../components/settings/DrawingSetting.js'; +import PaymentSetting from '../../components/settings/PaymentSetting.js'; const Setting = () => { const { t } = useTranslation(); @@ -63,6 +65,16 @@ const Setting = () => { content: , itemKey: 'drawing', }); + panes.push({ + tab: ( + + + {t('支付设置')} + + ), + content: , + itemKey: 'payment', + }); panes.push({ tab: ( From 2902d6c7c27e94ffe8483ab7f2c930c11620ff1b Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Sat, 21 Jun 2025 04:25:56 +0800 Subject: [PATCH 15/18] =?UTF-8?q?=F0=9F=8E=A8=20style:=20add=20`mt-2`=20st?= =?UTF-8?q?yle=20in=20ratio=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/settings/RatioSetting.js | 2 +- web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js | 2 +- web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/src/components/settings/RatioSetting.js b/web/src/components/settings/RatioSetting.js index 1d87c6de..99a6a3cf 100644 --- a/web/src/components/settings/RatioSetting.js +++ b/web/src/components/settings/RatioSetting.js @@ -82,7 +82,7 @@ const RatioSetting = () => { {/* 模型倍率设置以及可视化编辑器 */} - + diff --git a/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js b/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js index d5d8d832..25c67eee 100644 --- a/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js +++ b/web/src/pages/Setting/Ratio/ModelRationNotSetEditor.js @@ -372,7 +372,7 @@ export default function ModelRatioNotSetEditor(props) { return ( <> - + diff --git a/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js b/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js index 85426a5e..983f3afe 100644 --- a/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js +++ b/web/src/pages/Setting/Ratio/ModelSettingsVisualEditor.js @@ -404,7 +404,7 @@ export default function ModelSettingsVisualEditor(props) { return ( <> - +