| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import React, { useState, useEffect } from 'react'; |
| | import { |
| | TextArea, |
| | Typography, |
| | Button, |
| | Switch, |
| | Banner, |
| | } from '@douyinfe/semi-ui'; |
| | import { Code, Edit, Check, X, AlertTriangle } from 'lucide-react'; |
| | import { useTranslation } from 'react-i18next'; |
| |
|
| | const CustomRequestEditor = ({ |
| | customRequestMode, |
| | customRequestBody, |
| | onCustomRequestModeChange, |
| | onCustomRequestBodyChange, |
| | defaultPayload, |
| | }) => { |
| | const { t } = useTranslation(); |
| | const [isValid, setIsValid] = useState(true); |
| | const [errorMessage, setErrorMessage] = useState(''); |
| | const [localValue, setLocalValue] = useState(customRequestBody || ''); |
| |
|
| | |
| | useEffect(() => { |
| | if ( |
| | customRequestMode && |
| | (!customRequestBody || customRequestBody.trim() === '') |
| | ) { |
| | const defaultJson = defaultPayload |
| | ? JSON.stringify(defaultPayload, null, 2) |
| | : ''; |
| | setLocalValue(defaultJson); |
| | onCustomRequestBodyChange(defaultJson); |
| | } |
| | }, [ |
| | customRequestMode, |
| | defaultPayload, |
| | customRequestBody, |
| | onCustomRequestBodyChange, |
| | ]); |
| |
|
| | |
| | useEffect(() => { |
| | if (customRequestBody !== localValue) { |
| | setLocalValue(customRequestBody || ''); |
| | validateJson(customRequestBody || ''); |
| | } |
| | }, [customRequestBody]); |
| |
|
| | |
| | const validateJson = (value) => { |
| | if (!value.trim()) { |
| | setIsValid(true); |
| | setErrorMessage(''); |
| | return true; |
| | } |
| |
|
| | try { |
| | JSON.parse(value); |
| | setIsValid(true); |
| | setErrorMessage(''); |
| | return true; |
| | } catch (error) { |
| | setIsValid(false); |
| | setErrorMessage(`JSON格式错误: ${error.message}`); |
| | return false; |
| | } |
| | }; |
| |
|
| | const handleValueChange = (value) => { |
| | setLocalValue(value); |
| | validateJson(value); |
| | |
| | onCustomRequestBodyChange(value); |
| | }; |
| |
|
| | const handleModeToggle = (enabled) => { |
| | onCustomRequestModeChange(enabled); |
| | if (enabled && defaultPayload) { |
| | const defaultJson = JSON.stringify(defaultPayload, null, 2); |
| | setLocalValue(defaultJson); |
| | onCustomRequestBodyChange(defaultJson); |
| | } |
| | }; |
| |
|
| | const formatJson = () => { |
| | try { |
| | const parsed = JSON.parse(localValue); |
| | const formatted = JSON.stringify(parsed, null, 2); |
| | setLocalValue(formatted); |
| | onCustomRequestBodyChange(formatted); |
| | setIsValid(true); |
| | setErrorMessage(''); |
| | } catch (error) { |
| | |
| | } |
| | }; |
| |
|
| | return ( |
| | <div className='space-y-4'> |
| | {/* 自定义模式开关 */} |
| | <div className='flex items-center justify-between'> |
| | <div className='flex items-center gap-2'> |
| | <Code size={16} className='text-gray-500' /> |
| | <Typography.Text strong className='text-sm'> |
| | 自定义请求体模式 |
| | </Typography.Text> |
| | </div> |
| | <Switch |
| | checked={customRequestMode} |
| | onChange={handleModeToggle} |
| | checkedText='开' |
| | uncheckedText='关' |
| | size='small' |
| | /> |
| | </div> |
| | |
| | {customRequestMode && ( |
| | <> |
| | {/* 提示信息 */} |
| | <Banner |
| | type='warning' |
| | description='启用此模式后,将使用您自定义的请求体发送API请求,模型配置面板的参数设置将被忽略。' |
| | icon={<AlertTriangle size={16} />} |
| | className='!rounded-lg' |
| | closeIcon={null} |
| | /> |
| | |
| | {/* JSON编辑器 */} |
| | <div> |
| | <div className='flex items-center justify-between mb-2'> |
| | <Typography.Text strong className='text-sm'> |
| | 请求体 JSON |
| | </Typography.Text> |
| | <div className='flex items-center gap-2'> |
| | {isValid ? ( |
| | <div className='flex items-center gap-1 text-green-600'> |
| | <Check size={14} /> |
| | <Typography.Text className='text-xs'> |
| | 格式正确 |
| | </Typography.Text> |
| | </div> |
| | ) : ( |
| | <div className='flex items-center gap-1 text-red-600'> |
| | <X size={14} /> |
| | <Typography.Text className='text-xs'> |
| | 格式错误 |
| | </Typography.Text> |
| | </div> |
| | )} |
| | <Button |
| | theme='borderless' |
| | type='tertiary' |
| | size='small' |
| | icon={<Edit size={14} />} |
| | onClick={formatJson} |
| | disabled={!isValid} |
| | className='!rounded-lg' |
| | > |
| | 格式化 |
| | </Button> |
| | </div> |
| | </div> |
| | |
| | <TextArea |
| | value={localValue} |
| | onChange={handleValueChange} |
| | placeholder='{"model": "gpt-4o", "messages": [...], ...}' |
| | autosize={{ minRows: 8, maxRows: 20 }} |
| | className={`custom-request-textarea !rounded-lg font-mono text-sm ${!isValid ? '!border-red-500' : ''}`} |
| | style={{ |
| | fontFamily: 'Consolas, Monaco, "Courier New", monospace', |
| | lineHeight: '1.5', |
| | }} |
| | /> |
| | |
| | {!isValid && errorMessage && ( |
| | <Typography.Text type='danger' className='text-xs mt-1 block'> |
| | {errorMessage} |
| | </Typography.Text> |
| | )} |
| | |
| | <Typography.Text className='text-xs text-gray-500 mt-2 block'> |
| | 请输入有效的JSON格式的请求体。您可以参考预览面板中的默认请求体格式。 |
| | </Typography.Text> |
| | </div> |
| | </> |
| | )} |
| | </div> |
| | ); |
| | }; |
| |
|
| | export default CustomRequestEditor; |
| |
|