| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- /*
- 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, modelSelectFilter } 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,
- onInputChange,
- onParameterToggle,
- onCloseSettings,
- onConfigImport,
- onConfigReset,
- onCustomRequestModeChange,
- onCustomRequestBodyChange,
- previewPayload,
- messages,
- }) => {
- 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">
- (已在自定义模式中忽略)
- </Typography.Text>
- )}
- </div>
- <Select
- placeholder={t('请选择分组')}
- name='group'
- required
- selection
- 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">
- (已在自定义模式中忽略)
- </Typography.Text>
- )}
- </div>
- <Select
- placeholder={t('请选择模型')}
- name='model'
- required
- selection
- filter={modelSelectFilter}
- 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>
- {/* 图片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}
- />
- </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">
- 流式输出
- </Typography.Text>
- {customRequestMode && (
- <Typography.Text className="text-xs text-orange-600">
- (已在自定义模式中忽略)
- </Typography.Text>
- )}
- </div>
- <Switch
- checked={inputs.stream}
- onChange={(checked) => onInputChange('stream', checked)}
- checkedText="开"
- uncheckedText="关"
- 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;
|