Merge branch 'main-upstream' into pr/custom-currency-1923
# Conflicts: # web/src/components/settings/personal/cards/AccountManagement.jsx # web/src/components/table/channels/modals/EditChannelModal.jsx # web/src/hooks/channels/useChannelsData.jsx # web/src/hooks/common/useSidebar.js # web/src/i18n/locales/fr.json # web/src/pages/Setting/Operation/SettingsGeneral.jsx
This commit is contained in:
@@ -56,8 +56,10 @@ import {
|
||||
} from '../../../../helpers';
|
||||
import ModelSelectModal from './ModelSelectModal';
|
||||
import JSONEditor from '../../../common/ui/JSONEditor';
|
||||
import TwoFactorAuthModal from '../../../common/modals/TwoFactorAuthModal';
|
||||
import SecureVerificationModal from '../../../common/modals/SecureVerificationModal';
|
||||
import ChannelKeyDisplay from '../../../common/ui/ChannelKeyDisplay';
|
||||
import { useSecureVerification } from '../../../../hooks/common/useSecureVerification';
|
||||
import { createApiCalls } from '../../../../services/secureVerification';
|
||||
import {
|
||||
IconSave,
|
||||
IconClose,
|
||||
@@ -66,6 +68,8 @@ import {
|
||||
IconCode,
|
||||
IconGlobe,
|
||||
IconBolt,
|
||||
IconChevronUp,
|
||||
IconChevronDown,
|
||||
} from '@douyinfe/semi-icons';
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
@@ -151,6 +155,10 @@ const EditChannelModal = (props) => {
|
||||
vertex_key_type: 'json',
|
||||
// 企业账户设置
|
||||
is_enterprise_account: false,
|
||||
// 字段透传控制默认值
|
||||
allow_service_tier: false,
|
||||
disable_store: false, // false = 允许透传(默认开启)
|
||||
allow_safety_identifier: false,
|
||||
};
|
||||
const [batch, setBatch] = useState(false);
|
||||
const [multiToSingle, setMultiToSingle] = useState(false);
|
||||
@@ -178,12 +186,9 @@ const EditChannelModal = (props) => {
|
||||
const [keyMode, setKeyMode] = useState('append'); // 密钥模式:replace(覆盖)或 append(追加)
|
||||
const [isEnterpriseAccount, setIsEnterpriseAccount] = useState(false); // 是否为企业账户
|
||||
|
||||
// 2FA验证查看密钥相关状态
|
||||
const [twoFAState, setTwoFAState] = useState({
|
||||
// 密钥显示状态
|
||||
const [keyDisplayState, setKeyDisplayState] = useState({
|
||||
showModal: false,
|
||||
code: '',
|
||||
loading: false,
|
||||
showKey: false,
|
||||
keyData: '',
|
||||
});
|
||||
|
||||
@@ -192,18 +197,57 @@ const EditChannelModal = (props) => {
|
||||
const [verifyCode, setVerifyCode] = useState('');
|
||||
const [verifyLoading, setVerifyLoading] = useState(false);
|
||||
|
||||
// 表单块导航相关状态
|
||||
const formSectionRefs = useRef({
|
||||
basicInfo: null,
|
||||
apiConfig: null,
|
||||
modelConfig: null,
|
||||
advancedSettings: null,
|
||||
channelExtraSettings: null,
|
||||
});
|
||||
const [currentSectionIndex, setCurrentSectionIndex] = useState(0);
|
||||
const formSections = ['basicInfo', 'apiConfig', 'modelConfig', 'advancedSettings', 'channelExtraSettings'];
|
||||
const formContainerRef = useRef(null);
|
||||
|
||||
// 2FA状态更新辅助函数
|
||||
const updateTwoFAState = (updates) => {
|
||||
setTwoFAState((prev) => ({ ...prev, ...updates }));
|
||||
};
|
||||
// 使用通用安全验证 Hook
|
||||
const {
|
||||
isModalVisible,
|
||||
verificationMethods,
|
||||
verificationState,
|
||||
withVerification,
|
||||
executeVerification,
|
||||
cancelVerification,
|
||||
setVerificationCode,
|
||||
switchVerificationMethod,
|
||||
} = useSecureVerification({
|
||||
onSuccess: (result) => {
|
||||
// 验证成功后显示密钥
|
||||
console.log('Verification success, result:', result);
|
||||
if (result && result.success && result.data?.key) {
|
||||
showSuccess(t('密钥获取成功'));
|
||||
setKeyDisplayState({
|
||||
showModal: true,
|
||||
keyData: result.data.key,
|
||||
});
|
||||
} else if (result && result.key) {
|
||||
// 直接返回了 key(没有包装在 data 中)
|
||||
showSuccess(t('密钥获取成功'));
|
||||
setKeyDisplayState({
|
||||
showModal: true,
|
||||
keyData: result.key,
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// 重置2FA状态
|
||||
const resetTwoFAState = () => {
|
||||
setTwoFAState({
|
||||
// 重置密钥显示状态
|
||||
const resetKeyDisplayState = () => {
|
||||
setKeyDisplayState({
|
||||
showModal: false,
|
||||
code: '',
|
||||
loading: false,
|
||||
showKey: false,
|
||||
keyData: '',
|
||||
});
|
||||
};
|
||||
@@ -215,6 +259,37 @@ const EditChannelModal = (props) => {
|
||||
setVerifyLoading(false);
|
||||
};
|
||||
|
||||
// 表单导航功能
|
||||
const scrollToSection = (sectionKey) => {
|
||||
const sectionElement = formSectionRefs.current[sectionKey];
|
||||
if (sectionElement) {
|
||||
sectionElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start',
|
||||
inline: 'nearest'
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const navigateToSection = (direction) => {
|
||||
const availableSections = formSections.filter(section => {
|
||||
if (section === 'apiConfig') {
|
||||
return showApiConfigCard;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
let newIndex;
|
||||
if (direction === 'up') {
|
||||
newIndex = currentSectionIndex > 0 ? currentSectionIndex - 1 : availableSections.length - 1;
|
||||
} else {
|
||||
newIndex = currentSectionIndex < availableSections.length - 1 ? currentSectionIndex + 1 : 0;
|
||||
}
|
||||
|
||||
setCurrentSectionIndex(newIndex);
|
||||
scrollToSection(availableSections[newIndex]);
|
||||
};
|
||||
|
||||
// 渠道额外设置状态
|
||||
const [channelSettings, setChannelSettings] = useState({
|
||||
force_format: false,
|
||||
@@ -431,17 +506,27 @@ const EditChannelModal = (props) => {
|
||||
// 读取企业账户设置
|
||||
data.is_enterprise_account =
|
||||
parsedSettings.openrouter_enterprise === true;
|
||||
// 读取字段透传控制设置
|
||||
data.allow_service_tier = parsedSettings.allow_service_tier || false;
|
||||
data.disable_store = parsedSettings.disable_store || false;
|
||||
data.allow_safety_identifier = parsedSettings.allow_safety_identifier || false;
|
||||
} catch (error) {
|
||||
console.error('解析其他设置失败:', error);
|
||||
data.azure_responses_version = '';
|
||||
data.region = '';
|
||||
data.vertex_key_type = 'json';
|
||||
data.is_enterprise_account = false;
|
||||
data.allow_service_tier = false;
|
||||
data.disable_store = false;
|
||||
data.allow_safety_identifier = false;
|
||||
}
|
||||
} else {
|
||||
// 兼容历史数据:老渠道没有 settings 时,默认按 json 展示
|
||||
data.vertex_key_type = 'json';
|
||||
data.is_enterprise_account = false;
|
||||
data.allow_service_tier = false;
|
||||
data.disable_store = false;
|
||||
data.allow_safety_identifier = false;
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -591,42 +676,33 @@ const EditChannelModal = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 使用TwoFactorAuthModal的验证函数
|
||||
const handleVerify2FA = async () => {
|
||||
if (!verifyCode) {
|
||||
showError(t('请输入验证码或备用码'));
|
||||
return;
|
||||
}
|
||||
|
||||
setVerifyLoading(true);
|
||||
// 查看渠道密钥(透明验证)
|
||||
const handleShow2FAModal = async () => {
|
||||
try {
|
||||
const res = await API.post(`/api/channel/${channelId}/key`, {
|
||||
code: verifyCode,
|
||||
});
|
||||
if (res.data.success) {
|
||||
// 验证成功,显示密钥
|
||||
updateTwoFAState({
|
||||
// 使用 withVerification 包装,会自动处理需要验证的情况
|
||||
const result = await withVerification(
|
||||
createApiCalls.viewChannelKey(channelId),
|
||||
{
|
||||
title: t('查看渠道密钥'),
|
||||
description: t('为了保护账户安全,请验证您的身份。'),
|
||||
preferredMethod: 'passkey', // 优先使用 Passkey
|
||||
}
|
||||
);
|
||||
|
||||
// 如果直接返回了结果(已验证),显示密钥
|
||||
if (result && result.success && result.data?.key) {
|
||||
showSuccess(t('密钥获取成功'));
|
||||
setKeyDisplayState({
|
||||
showModal: true,
|
||||
showKey: true,
|
||||
keyData: res.data.data.key,
|
||||
keyData: result.data.key,
|
||||
});
|
||||
reset2FAVerifyState();
|
||||
showSuccess(t('验证成功'));
|
||||
} else {
|
||||
showError(res.data.message);
|
||||
}
|
||||
} catch (error) {
|
||||
showError(t('获取密钥失败'));
|
||||
} finally {
|
||||
setVerifyLoading(false);
|
||||
console.error('Failed to view channel key:', error);
|
||||
showError(error.message || t('获取密钥失败'));
|
||||
}
|
||||
};
|
||||
|
||||
// 显示2FA验证模态框 - 使用TwoFactorAuthModal
|
||||
const handleShow2FAModal = () => {
|
||||
setShow2FAVerifyModal(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const modelMap = new Map();
|
||||
|
||||
@@ -702,6 +778,8 @@ const EditChannelModal = (props) => {
|
||||
fetchModelGroups();
|
||||
// 重置手动输入模式状态
|
||||
setUseManualInput(false);
|
||||
// 重置导航状态
|
||||
setCurrentSectionIndex(0);
|
||||
} else {
|
||||
// 统一的模态框关闭重置逻辑
|
||||
resetModalState();
|
||||
@@ -730,10 +808,8 @@ const EditChannelModal = (props) => {
|
||||
}
|
||||
// 重置本地输入,避免下次打开残留上一次的 JSON 字段值
|
||||
setInputs(getInitValues());
|
||||
// 重置2FA状态
|
||||
resetTwoFAState();
|
||||
// 重置2FA验证状态
|
||||
reset2FAVerifyState();
|
||||
// 重置密钥显示状态
|
||||
resetKeyDisplayState();
|
||||
};
|
||||
|
||||
const handleVertexUploadChange = ({ fileList }) => {
|
||||
@@ -892,22 +968,34 @@ const EditChannelModal = (props) => {
|
||||
};
|
||||
localInputs.setting = JSON.stringify(channelExtraSettings);
|
||||
|
||||
// 处理type === 20的企业账户设置
|
||||
if (localInputs.type === 20) {
|
||||
let settings = {};
|
||||
if (localInputs.settings) {
|
||||
try {
|
||||
settings = JSON.parse(localInputs.settings);
|
||||
} catch (error) {
|
||||
console.error('解析settings失败:', error);
|
||||
}
|
||||
// 处理 settings 字段(包括企业账户设置和字段透传控制)
|
||||
let settings = {};
|
||||
if (localInputs.settings) {
|
||||
try {
|
||||
settings = JSON.parse(localInputs.settings);
|
||||
} catch (error) {
|
||||
console.error('解析settings失败:', error);
|
||||
}
|
||||
// 设置企业账户标识,无论是true还是false都要传到后端
|
||||
}
|
||||
|
||||
// type === 20: 设置企业账户标识,无论是true还是false都要传到后端
|
||||
if (localInputs.type === 20) {
|
||||
settings.openrouter_enterprise =
|
||||
localInputs.is_enterprise_account === true;
|
||||
localInputs.settings = JSON.stringify(settings);
|
||||
}
|
||||
|
||||
// type === 1 (OpenAI) 或 type === 14 (Claude): 设置字段透传控制(显式保存布尔值)
|
||||
if (localInputs.type === 1 || localInputs.type === 14) {
|
||||
settings.allow_service_tier = localInputs.allow_service_tier === true;
|
||||
// 仅 OpenAI 渠道需要 store 和 safety_identifier
|
||||
if (localInputs.type === 1) {
|
||||
settings.disable_store = localInputs.disable_store === true;
|
||||
settings.allow_safety_identifier = localInputs.allow_safety_identifier === true;
|
||||
}
|
||||
}
|
||||
|
||||
localInputs.settings = JSON.stringify(settings);
|
||||
|
||||
// 清理不需要发送到后端的字段
|
||||
delete localInputs.force_format;
|
||||
delete localInputs.thinking_to_content;
|
||||
@@ -918,6 +1006,10 @@ const EditChannelModal = (props) => {
|
||||
delete localInputs.is_enterprise_account;
|
||||
// 顶层的 vertex_key_type 不应发送给后端
|
||||
delete localInputs.vertex_key_type;
|
||||
// 清理字段透传控制的临时字段
|
||||
delete localInputs.allow_service_tier;
|
||||
delete localInputs.disable_store;
|
||||
delete localInputs.allow_safety_identifier;
|
||||
|
||||
let res;
|
||||
localInputs.auto_ban = localInputs.auto_ban ? 1 : 0;
|
||||
@@ -1233,7 +1325,41 @@ const EditChannelModal = (props) => {
|
||||
visible={props.visible}
|
||||
width={isMobile ? '100%' : 600}
|
||||
footer={
|
||||
<div className='flex justify-end bg-white'>
|
||||
<div className='flex justify-between items-center bg-white'>
|
||||
<div className='flex gap-2'>
|
||||
<Button
|
||||
size='small'
|
||||
type='tertiary'
|
||||
icon={<IconChevronUp />}
|
||||
onClick={() => navigateToSection('up')}
|
||||
style={{
|
||||
borderRadius: '50%',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
title={t('上一个表单块')}
|
||||
/>
|
||||
<Button
|
||||
size='small'
|
||||
type='tertiary'
|
||||
icon={<IconChevronDown />}
|
||||
onClick={() => navigateToSection('down')}
|
||||
style={{
|
||||
borderRadius: '50%',
|
||||
width: '32px',
|
||||
height: '32px',
|
||||
padding: 0,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
title={t('下一个表单块')}
|
||||
/>
|
||||
</div>
|
||||
<Space>
|
||||
<Button
|
||||
theme='solid'
|
||||
@@ -1264,10 +1390,14 @@ const EditChannelModal = (props) => {
|
||||
>
|
||||
{() => (
|
||||
<Spin spinning={loading}>
|
||||
<div className='p-2'>
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Basic Info */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<div
|
||||
className='p-2'
|
||||
ref={formContainerRef}
|
||||
>
|
||||
<div ref={el => formSectionRefs.current.basicInfo = el}>
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Basic Info */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<Avatar
|
||||
size='small'
|
||||
color='blue'
|
||||
@@ -1743,13 +1873,15 @@ const EditChannelModal = (props) => {
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* API Configuration Card */}
|
||||
{showApiConfigCard && (
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: API Config */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<div ref={el => formSectionRefs.current.apiConfig = el}>
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: API Config */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<Avatar
|
||||
size='small'
|
||||
color='green'
|
||||
@@ -1960,13 +2092,15 @@ const EditChannelModal = (props) => {
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Model Configuration Card */}
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Model Config */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<div ref={el => formSectionRefs.current.modelConfig = el}>
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Model Config */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<Avatar
|
||||
size='small'
|
||||
color='purple'
|
||||
@@ -2161,12 +2295,14 @@ const EditChannelModal = (props) => {
|
||||
formApi={formApiRef.current}
|
||||
extraText={t('键为请求中的模型名称,值为要替换的模型名称')}
|
||||
/>
|
||||
</Card>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Advanced Settings Card */}
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Advanced Settings */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<div ref={el => formSectionRefs.current.advancedSettings = el}>
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Advanced Settings */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<Avatar
|
||||
size='small'
|
||||
color='orange'
|
||||
@@ -2325,32 +2461,44 @@ const EditChannelModal = (props) => {
|
||||
t('此项可选,用于覆盖请求头参数') +
|
||||
'\n' +
|
||||
t('格式示例:') +
|
||||
'\n{\n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0"\n}'
|
||||
'\n{\n "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0",\n "Authorization": "Bearer {api_key}"\n}'
|
||||
}
|
||||
autosize
|
||||
onChange={(value) =>
|
||||
handleInputChange('header_override', value)
|
||||
}
|
||||
extraText={
|
||||
<div className='flex gap-2 flex-wrap'>
|
||||
<Text
|
||||
className='!text-semi-color-primary cursor-pointer'
|
||||
onClick={() =>
|
||||
handleInputChange(
|
||||
'header_override',
|
||||
JSON.stringify(
|
||||
{
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0',
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
)
|
||||
}
|
||||
>
|
||||
{t('格式模板')}
|
||||
</Text>
|
||||
|
||||
<div className='flex flex-col gap-1'>
|
||||
<div className='flex gap-2 flex-wrap items-center'>
|
||||
<Text
|
||||
className='!text-semi-color-primary cursor-pointer'
|
||||
onClick={() =>
|
||||
handleInputChange(
|
||||
'header_override',
|
||||
JSON.stringify(
|
||||
{
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36 Edg/139.0.0.0',
|
||||
'Authorization': 'Bearer{api_key}',
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
)
|
||||
}
|
||||
>
|
||||
{t('填入模板')}
|
||||
</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text type='tertiary' size='small'>
|
||||
{t('支持变量:')}
|
||||
</Text>
|
||||
<div className='text-xs text-tertiary ml-2'>
|
||||
<div>{t('渠道密钥')}: {'{api_key}'}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
showClear
|
||||
@@ -2379,12 +2527,84 @@ const EditChannelModal = (props) => {
|
||||
'键为原状态码,值为要复写的状态码,仅影响本地判断',
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
{/* 字段透传控制 - OpenAI 渠道 */}
|
||||
{inputs.type === 1 && (
|
||||
<>
|
||||
<div className='mt-4 mb-2 text-sm font-medium text-gray-700'>
|
||||
{t('字段透传控制')}
|
||||
</div>
|
||||
|
||||
<Form.Switch
|
||||
field='allow_service_tier'
|
||||
label={t('允许 service_tier 透传')}
|
||||
checkedText={t('开')}
|
||||
uncheckedText={t('关')}
|
||||
onChange={(value) =>
|
||||
handleChannelOtherSettingsChange('allow_service_tier', value)
|
||||
}
|
||||
extraText={t(
|
||||
'service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用',
|
||||
)}
|
||||
/>
|
||||
|
||||
<Form.Switch
|
||||
field='disable_store'
|
||||
label={t('禁用 store 透传')}
|
||||
checkedText={t('开')}
|
||||
uncheckedText={t('关')}
|
||||
onChange={(value) =>
|
||||
handleChannelOtherSettingsChange('disable_store', value)
|
||||
}
|
||||
extraText={t(
|
||||
'store 字段用于授权 OpenAI 存储请求数据以评估和优化产品。默认关闭,开启后可能导致 Codex 无法正常使用',
|
||||
)}
|
||||
/>
|
||||
|
||||
<Form.Switch
|
||||
field='allow_safety_identifier'
|
||||
label={t('允许 safety_identifier 透传')}
|
||||
checkedText={t('开')}
|
||||
uncheckedText={t('关')}
|
||||
onChange={(value) =>
|
||||
handleChannelOtherSettingsChange('allow_safety_identifier', value)
|
||||
}
|
||||
extraText={t(
|
||||
'safety_identifier 字段用于帮助 OpenAI 识别可能违反使用政策的应用程序用户。默认关闭以保护用户隐私',
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 字段透传控制 - Claude 渠道 */}
|
||||
{(inputs.type === 14) && (
|
||||
<>
|
||||
<div className='mt-4 mb-2 text-sm font-medium text-gray-700'>
|
||||
{t('字段透传控制')}
|
||||
</div>
|
||||
|
||||
<Form.Switch
|
||||
field='allow_service_tier'
|
||||
label={t('允许 service_tier 透传')}
|
||||
checkedText={t('开')}
|
||||
uncheckedText={t('关')}
|
||||
onChange={(value) =>
|
||||
handleChannelOtherSettingsChange('allow_service_tier', value)
|
||||
}
|
||||
extraText={t(
|
||||
'service_tier 字段用于指定服务层级,允许透传可能导致实际计费高于预期。默认关闭以避免额外费用',
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Channel Extra Settings Card */}
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Channel Extra Settings */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<div ref={el => formSectionRefs.current.channelExtraSettings = el}>
|
||||
<Card className='!rounded-2xl shadow-sm border-0 mb-6'>
|
||||
{/* Header: Channel Extra Settings */}
|
||||
<div className='flex items-center mb-2'>
|
||||
<Avatar
|
||||
size='small'
|
||||
color='violet'
|
||||
@@ -2482,7 +2702,8 @@ const EditChannelModal = (props) => {
|
||||
'如果用户请求中包含系统提示词,则使用此设置拼接到用户的系统提示词前面',
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</Spin>
|
||||
)}
|
||||
@@ -2493,17 +2714,17 @@ const EditChannelModal = (props) => {
|
||||
onVisibleChange={(visible) => setIsModalOpenurl(visible)}
|
||||
/>
|
||||
</SideSheet>
|
||||
{/* 使用TwoFactorAuthModal组件进行2FA验证 */}
|
||||
<TwoFactorAuthModal
|
||||
visible={show2FAVerifyModal}
|
||||
code={verifyCode}
|
||||
loading={verifyLoading}
|
||||
onCodeChange={setVerifyCode}
|
||||
onVerify={handleVerify2FA}
|
||||
onCancel={reset2FAVerifyState}
|
||||
title={t('查看渠道密钥')}
|
||||
description={t('为了保护账户安全,请验证您的两步验证码。')}
|
||||
placeholder={t('请输入验证码或备用码')}
|
||||
{/* 使用通用安全验证模态框 */}
|
||||
<SecureVerificationModal
|
||||
visible={isModalVisible}
|
||||
verificationMethods={verificationMethods}
|
||||
verificationState={verificationState}
|
||||
onVerify={executeVerification}
|
||||
onCancel={cancelVerification}
|
||||
onCodeChange={setVerificationCode}
|
||||
onMethodSwitch={switchVerificationMethod}
|
||||
title={verificationState.title}
|
||||
description={verificationState.description}
|
||||
/>
|
||||
|
||||
{/* 使用ChannelKeyDisplay组件显示密钥 */}
|
||||
@@ -2526,10 +2747,10 @@ const EditChannelModal = (props) => {
|
||||
{t('渠道密钥信息')}
|
||||
</div>
|
||||
}
|
||||
visible={twoFAState.showModal && twoFAState.showKey}
|
||||
onCancel={resetTwoFAState}
|
||||
visible={keyDisplayState.showModal}
|
||||
onCancel={resetKeyDisplayState}
|
||||
footer={
|
||||
<Button type='primary' onClick={resetTwoFAState}>
|
||||
<Button type='primary' onClick={resetKeyDisplayState}>
|
||||
{t('完成')}
|
||||
</Button>
|
||||
}
|
||||
@@ -2537,7 +2758,7 @@ const EditChannelModal = (props) => {
|
||||
style={{ maxWidth: '90vw' }}
|
||||
>
|
||||
<ChannelKeyDisplay
|
||||
keyData={twoFAState.keyData}
|
||||
keyData={keyDisplayState.keyData}
|
||||
showSuccessIcon={true}
|
||||
successText={t('密钥获取成功')}
|
||||
showWarning={true}
|
||||
|
||||
Reference in New Issue
Block a user