|
- /*
- 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
- */
-
- import React from 'react';
- import { Card, Select, Typography, Button, Switch } from '@douyinfe/semi-ui';
- import { Sparkles, Users, ToggleLeft, X, Settings } from 'lucide-react';
- import { useTranslation } from 'react-i18next';
- import { renderGroupOption, selectFilter } from '../../helpers';
- import ParameterControl from './ParameterControl';
- import ImageUrlInput from './ImageUrlInput';
- import ConfigManager from './ConfigManager';
- import CustomRequestEditor from './CustomRequestEditor';
-
- const SettingsPanel = ({
- inputs,
- parameterEnabled,
- models,
- groups,
- styleState,
- showDebugPanel,
- customRequestMode,
- customRequestBody,
- mutualExclusive = false,
- onInputChange,
- onParameterToggle,
- onCloseSettings,
- onConfigImport,
- onConfigReset,
- onCustomRequestModeChange,
- onCustomRequestBodyChange,
- previewPayload,
- messages,
- channels = [],
- }) => {
- const { t } = useTranslation();
-
- const currentConfig = {
- inputs,
- parameterEnabled,
- showDebugPanel,
- customRequestMode,
- customRequestBody,
- };
-
- return (
- <Card
- className='h-full flex flex-col'
- bordered={false}
- bodyStyle={{
- padding: styleState.isMobile ? '16px' : '24px',
- height: '100%',
- display: 'flex',
- flexDirection: 'column',
- }}
- >
- {/* 标题区域 - 与调试面板保持一致 */}
- <div className='flex items-center justify-between mb-6 flex-shrink-0'>
- <div className='flex items-center'>
- <div className='w-10 h-10 rounded-full bg-gradient-to-r from-purple-500 to-pink-500 flex items-center justify-center mr-3'>
- <Settings size={20} className='text-white' />
- </div>
- <Typography.Title heading={5} className='mb-0'>
- {t('模型配置')}
- </Typography.Title>
- </div>
-
- {styleState.isMobile && onCloseSettings && (
- <Button
- icon={<X size={16} />}
- onClick={onCloseSettings}
- theme='borderless'
- type='tertiary'
- size='small'
- className='!rounded-lg'
- />
- )}
- </div>
-
- {/* 移动端配置管理 */}
- {styleState.isMobile && (
- <div className='mb-4 flex-shrink-0'>
- <ConfigManager
- currentConfig={currentConfig}
- onConfigImport={onConfigImport}
- onConfigReset={onConfigReset}
- styleState={{ ...styleState, isMobile: false }}
- messages={messages}
- />
- </div>
- )}
-
- <div className='space-y-6 overflow-y-auto flex-1 pr-2 model-settings-scroll'>
- {/* 自定义请求体编辑器 */}
- <CustomRequestEditor
- customRequestMode={customRequestMode}
- customRequestBody={customRequestBody}
- onCustomRequestModeChange={onCustomRequestModeChange}
- onCustomRequestBodyChange={onCustomRequestBodyChange}
- defaultPayload={previewPayload}
- />
-
- {/* 分组选择 - 已隐藏
- <div className={customRequestMode ? 'opacity-50' : ''}>
- <div className='flex items-center gap-2 mb-2'>
- <Users size={16} className='text-gray-500' />
- <Typography.Text strong className='text-sm'>
- {t('分组')}
- </Typography.Text>
- {customRequestMode && (
- <Typography.Text className='text-xs text-orange-600'>
- ({t('已在自定义模式中忽略')})
- </Typography.Text>
- )}
- </div>
- <Select
- placeholder={t('请选择分组')}
- name='group'
- required
- selection
- filter={selectFilter}
- autoClearSearchValue={false}
- onChange={(value) => onInputChange('group', value)}
- value={inputs.group}
- autoComplete='new-password'
- optionList={groups}
- renderOptionItem={renderGroupOption}
- style={{ width: '100%' }}
- dropdownStyle={{ width: '100%', maxWidth: '100%' }}
- className='!rounded-lg'
- disabled={customRequestMode}
- />
- </div>
- */}
-
- {/* 模型选择 */}
- <div className={customRequestMode ? 'opacity-50' : ''}>
- <div className='flex items-center gap-2 mb-2'>
- <Sparkles size={16} className='text-gray-500' />
- <Typography.Text strong className='text-sm'>
- {t('模型')}
- </Typography.Text>
- {customRequestMode && (
- <Typography.Text className='text-xs text-orange-600'>
- ({t('已在自定义模式中忽略')})
- </Typography.Text>
- )}
- </div>
- <Select
- placeholder={t('请选择模型')}
- name='model'
- required
- selection
- filter={selectFilter}
- autoClearSearchValue={false}
- onChange={(value) => onInputChange('model', value)}
- value={inputs.model}
- autoComplete='new-password'
- optionList={models}
- style={{ width: '100%' }}
- dropdownStyle={{ width: '100%', maxWidth: '100%' }}
- className='!rounded-lg'
- disabled={customRequestMode}
- />
- </div>
-
- {/* 通道选择 */}
- <div className={customRequestMode ? 'opacity-50' : ''}>
- <div className='flex items-center gap-2 mb-2'>
- <Typography.Text strong className='text-sm'>
- {t('通道')}
- </Typography.Text>
- {customRequestMode && (
- <Typography.Text className='text-xs text-orange-600'>
- ({t('已在自定义模式中忽略')})
- </Typography.Text>
- )}
- </div>
- <Select
- placeholder={t('请选择通道')}
- name='channelId'
- selection
- filter={selectFilter}
- autoClearSearchValue={false}
- onChange={(value) => onInputChange('channelId', value)}
- value={inputs.channelId}
- autoComplete='new-password'
- optionList={channels.map((ch) => ({
- value: ch.id,
- label: ch.public_name || ch.name,
- }))}
- style={{ width: '100%' }}
- dropdownStyle={{ width: '100%', maxWidth: '100%' }}
- className='!rounded-lg'
- disabled={customRequestMode || channels.length === 0}
- />
- </div>
-
- {/* 图片URL输入 */}
- <div className={customRequestMode ? 'opacity-50' : ''}>
- <ImageUrlInput
- imageUrls={inputs.imageUrls}
- imageEnabled={inputs.imageEnabled}
- onImageUrlsChange={(urls) => onInputChange('imageUrls', urls)}
- onImageEnabledChange={(enabled) =>
- onInputChange('imageEnabled', enabled)
- }
- disabled={customRequestMode}
- />
- </div>
-
- {/* 参数控制组件 */}
- <div className={customRequestMode ? 'opacity-50' : ''}>
- <ParameterControl
- inputs={inputs}
- parameterEnabled={parameterEnabled}
- onInputChange={onInputChange}
- onParameterToggle={onParameterToggle}
- disabled={customRequestMode}
- mutualExclusive={mutualExclusive}
- />
- </div>
-
- {/* 流式输出开关 */}
- <div className={customRequestMode ? 'opacity-50' : ''}>
- <div className='flex items-center justify-between'>
- <div className='flex items-center gap-2'>
- <ToggleLeft size={16} className='text-gray-500' />
- <Typography.Text strong className='text-sm'>
- {t('流式输出')}
- </Typography.Text>
- {customRequestMode && (
- <Typography.Text className='text-xs text-orange-600'>
- ({t('已在自定义模式中忽略')})
- </Typography.Text>
- )}
- </div>
- <Switch
- checked={inputs.stream}
- onChange={(checked) => onInputChange('stream', checked)}
- checkedText={t('开')}
- uncheckedText={t('关')}
- size='small'
- disabled={customRequestMode}
- />
- </div>
- </div>
- </div>
-
- {/* 桌面端的配置管理放在底部 */}
- {!styleState.isMobile && (
- <div className='flex-shrink-0 pt-3'>
- <ConfigManager
- currentConfig={currentConfig}
- onConfigImport={onConfigImport}
- onConfigReset={onConfigReset}
- styleState={styleState}
- messages={messages}
- />
- </div>
- )}
- </Card>
- );
- };
-
- export default SettingsPanel;
|