new-api/web/src/components/settings/OperationSetting.jsx

157 lines
5.1 KiB
React
Raw Normal View History

/*
Copyright (C) 2025 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
2024-03-15 16:05:33 +08:00
import React, { useEffect, useState } from 'react';
import { Card, Spin } from '@douyinfe/semi-ui';
import SettingsGeneral from '../../pages/Setting/Operation/SettingsGeneral';
import SettingsHeaderNavModules from '../../pages/Setting/Operation/SettingsHeaderNavModules';
import SettingsSidebarModulesAdmin from '../../pages/Setting/Operation/SettingsSidebarModulesAdmin';
import SettingsSensitiveWords from '../../pages/Setting/Operation/SettingsSensitiveWords';
import SettingsLog from '../../pages/Setting/Operation/SettingsLog';
import SettingsMonitoring from '../../pages/Setting/Operation/SettingsMonitoring';
import SettingsCreditLimit from '../../pages/Setting/Operation/SettingsCreditLimit';
import SettingsCheckin from '../../pages/Setting/Operation/SettingsCheckin';
import { API, showError, toBoolean } from '../../helpers';
const OperationSetting = () => {
2024-03-15 16:05:33 +08:00
let [inputs, setInputs] = useState({
/* 额度相关 */
2024-03-15 16:05:33 +08:00
QuotaForNewUser: 0,
PreConsumedQuota: 0,
2024-03-15 16:05:33 +08:00
QuotaForInviter: 0,
QuotaForInvitee: 0,
2025-10-12 13:31:03 +08:00
'quota_setting.enable_free_model_pre_consume': true,
/* 通用设置 */
2024-03-15 16:05:33 +08:00
TopUpLink: '',
'general_setting.docs_link': '',
2024-03-15 16:05:33 +08:00
QuotaPerUnit: 0,
USDExchangeRate: 0,
RetryTimes: 0,
💱 feat(settings): introduce site-wide quota display type (USD/CNY/TOKENS/CUSTOM) Replace the legacy boolean “DisplayInCurrencyEnabled” with an injected, type-safe configuration `general_setting.quota_display_type`, and wire it through the backend and frontend. Backend - Add `QuotaDisplayType` to `operation_setting.GeneralSetting` with injected registration via `config.GlobalConfig.Register("general_setting", ...)`. Helpers: `IsCurrencyDisplay()`, `IsCNYDisplay()`, `GetQuotaDisplayType()`. - Expose `quota_display_type` in `/api/status` and keep legacy `display_in_currency` for backward compatibility. - Logger: update `LogQuota` and `FormatQuota` to support USD/CNY/TOKENS. When CNY is selected, convert using `operation_setting.USDExchangeRate`. - Controllers: - `billing`: compute subscription/usage amounts based on the selected type (USD: divide by `QuotaPerUnit`; CNY: USD→CNY; TOKENS: keep raw tokens). - `topup` / `topup_stripe`: treat inputs as “amount” for USD/CNY and as token-count for TOKENS; adjust min topup and pay money accordingly. - `misc`: include `quota_display_type` in status payload. - Compatibility: in `model/option.UpdateOption`, map updates to `DisplayInCurrencyEnabled` → `general_setting.quota_display_type` (true→USD, false→TOKENS). Keep exporting the legacy key in `OptionMap`. Frontend - Settings: replace the “display in currency” switch with a Select (`general_setting.quota_display_type`) offering USD / CNY / Tokens. Provide fallback mapping from legacy `DisplayInCurrencyEnabled`. - Persist `quota_display_type` to localStorage (keep `display_in_currency` for legacy components). - Rendering helpers: base all quota/price rendering on `quota_display_type`; use `usd_exchange_rate` for CNY symbol/values. - Pricing page: default view currency follows site display type (USD/CNY), while TOKENS mode still allows per-view currency toggling when needed. Notes - No database migrations required. - Legacy clients remain functional via compatibility fields.
2025-09-29 23:23:31 +08:00
'general_setting.quota_display_type': 'USD',
2024-05-08 14:57:36 +08:00
DisplayTokenStatEnabled: false,
DefaultCollapseSidebar: false,
DemoSiteEnabled: false,
SelfUseModeEnabled: false,
/* 顶栏模块管理 */
HeaderNavModules: '',
/* 左侧边栏模块管理(管理员) */
SidebarModulesAdmin: '',
/* 敏感词设置 */
CheckSensitiveEnabled: false,
CheckSensitiveOnPromptEnabled: false,
SensitiveWords: '',
/* 日志设置 */
LogConsumeEnabled: false,
/* 监控设置 */
ChannelDisableThreshold: 0,
QuotaRemindThreshold: 0,
AutomaticDisableChannelEnabled: false,
AutomaticEnableChannelEnabled: false,
AutomaticDisableKeywords: '',
'monitor_setting.auto_test_channel_enabled': false,
'monitor_setting.auto_test_channel_minutes': 10 /* 签到设置 */,
'checkin_setting.enabled': false,
'checkin_setting.min_quota': 1000,
'checkin_setting.max_quota': 10000,
2024-03-15 16:05:33 +08:00
});
2024-05-13 17:55:15 +08:00
2024-03-15 16:05:33 +08:00
let [loading, setLoading] = useState(false);
2024-03-15 16:05:33 +08:00
const getOptions = async () => {
const res = await API.get('/api/option/');
const { success, message, data } = res.data;
if (success) {
let newInputs = {};
data.forEach((item) => {
if (typeof inputs[item.key] === 'boolean') {
newInputs[item.key] = toBoolean(item.value);
2024-05-08 14:57:36 +08:00
} else {
newInputs[item.key] = item.value;
}
2024-03-15 16:05:33 +08:00
});
2024-05-08 14:57:36 +08:00
2024-03-15 16:05:33 +08:00
setInputs(newInputs);
} else {
showError(message);
}
};
2024-05-13 17:55:15 +08:00
async function onRefresh() {
try {
setLoading(true);
await getOptions();
// showSuccess('刷新成功');
2024-05-13 17:55:15 +08:00
} catch (error) {
showError('刷新失败');
} finally {
setLoading(false);
}
}
2024-03-15 16:05:33 +08:00
useEffect(() => {
2024-05-13 18:14:57 +08:00
onRefresh();
2024-03-15 16:05:33 +08:00
}, []);
2024-03-15 16:05:33 +08:00
return (
2024-05-08 14:57:36 +08:00
<>
2024-05-13 17:55:15 +08:00
<Spin spinning={loading} size='large'>
{/* 通用设置 */}
<Card style={{ marginTop: '10px' }}>
2024-05-13 18:14:57 +08:00
<SettingsGeneral options={inputs} refresh={onRefresh} />
2024-05-13 17:55:15 +08:00
</Card>
{/* 顶栏模块管理 */}
<div style={{ marginTop: '10px' }}>
<SettingsHeaderNavModules options={inputs} refresh={onRefresh} />
</div>
{/* 左侧边栏模块管理(管理员) */}
<div style={{ marginTop: '10px' }}>
<SettingsSidebarModulesAdmin options={inputs} refresh={onRefresh} />
</div>
2024-05-13 17:55:15 +08:00
{/* 屏蔽词过滤设置 */}
<Card style={{ marginTop: '10px' }}>
2024-05-13 18:14:57 +08:00
<SettingsSensitiveWords options={inputs} refresh={onRefresh} />
2024-05-13 17:55:15 +08:00
</Card>
{/* 日志设置 */}
<Card style={{ marginTop: '10px' }}>
2024-05-13 18:14:57 +08:00
<SettingsLog options={inputs} refresh={onRefresh} />
2024-05-13 17:55:15 +08:00
</Card>
{/* 监控设置 */}
<Card style={{ marginTop: '10px' }}>
2024-05-13 18:14:57 +08:00
<SettingsMonitoring options={inputs} refresh={onRefresh} />
2024-05-13 17:55:15 +08:00
</Card>
{/* 额度设置 */}
<Card style={{ marginTop: '10px' }}>
2024-05-13 18:14:57 +08:00
<SettingsCreditLimit options={inputs} refresh={onRefresh} />
2024-05-13 17:55:15 +08:00
</Card>
{/* 签到设置 */}
<Card style={{ marginTop: '10px' }}>
<SettingsCheckin options={inputs} refresh={onRefresh} />
</Card>
2024-05-13 17:55:15 +08:00
</Spin>
2024-05-08 14:57:36 +08:00
</>
2024-03-23 21:24:39 +08:00
);
};
export default OperationSetting;