From a5da09dfb9bca75be6ac2ec8927f0e9eafd4cb9f Mon Sep 17 00:00:00 2001 From: CaIon Date: Thu, 17 Jul 2025 19:53:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E6=89=8B=E5=8A=A8?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E5=92=8C=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E5=88=87=E6=8D=A2=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=AF=86=E9=92=A5=E6=90=9C=E7=B4=A2=E5=92=8C?= =?UTF-8?q?=E9=AB=98=E4=BA=AE=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/pages/Channel/EditChannel.js | 294 +++++++++++++++++++++------ 1 file changed, 227 insertions(+), 67 deletions(-) diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index ff66d65f..0934d891 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -26,6 +26,7 @@ import { Form, Row, Col, + Highlight, } from '@douyinfe/semi-ui'; import { getChannelModels, copy, getChannelIcon, getModelCategories } from '../../helpers'; import { @@ -122,6 +123,8 @@ const EditChannel = (props) => { const [vertexFileList, setVertexFileList] = useState([]); const vertexErroredNames = useRef(new Set()); // 避免重复报错 const [isMultiKeyChannel, setIsMultiKeyChannel] = useState(false); + const [channelSearchValue, setChannelSearchValue] = useState(''); + const [useManualInput, setUseManualInput] = useState(false); // 是否使用手动输入模式 const getInitValues = () => ({ ...originInputs }); const handleInputChange = (name, value) => { if (formApiRef.current) { @@ -190,6 +193,9 @@ const EditChannel = (props) => { setInputs((inputs) => ({ ...inputs, models: localModels })); } setBasicModels(localModels); + + // 重置手动输入模式状态 + setUseManualInput(false); } //setAutoBan }; @@ -418,6 +424,8 @@ const EditChannel = (props) => { } else { formApiRef.current?.setValues(getInitValues()); } + // 重置手动输入模式状态 + setUseManualInput(false); } else { formApiRef.current?.reset(); } @@ -468,41 +476,60 @@ const EditChannel = (props) => { let localInputs = { ...formValues }; if (localInputs.type === 41) { - let keys = vertexKeys; - - // 若当前未选择文件,尝试从已上传文件列表解析(异步读取) - if (keys.length === 0 && vertexFileList.length > 0) { - try { - const parsed = await Promise.all( - vertexFileList.map(async (item) => { - const fileObj = item.fileInstance; - if (!fileObj) return null; - const txt = await fileObj.text(); - return JSON.parse(txt); - }) - ); - keys = parsed.filter(Boolean); - } catch (err) { - showError(t('解析密钥文件失败: {{msg}}', { msg: err.message })); + if (useManualInput) { + // 手动输入模式 + if (localInputs.key && localInputs.key.trim() !== '') { + try { + // 验证 JSON 格式 + const parsedKey = JSON.parse(localInputs.key); + // 确保是有效的密钥格式 + localInputs.key = JSON.stringify(parsedKey); + } catch (err) { + showError(t('密钥格式无效,请输入有效的 JSON 格式密钥')); + return; + } + } else if (!isEdit) { + showInfo(t('请输入密钥!')); return; } - } - - // 创建模式必须上传密钥;编辑模式可选 - if (keys.length === 0) { - if (!isEdit) { - showInfo(t('请上传密钥文件!')); - return; - } else { - // 编辑模式且未上传新密钥,不修改 key - delete localInputs.key; - } } else { - // 有新密钥,则覆盖 - if (batch) { - localInputs.key = JSON.stringify(keys); + // 文件上传模式 + let keys = vertexKeys; + + // 若当前未选择文件,尝试从已上传文件列表解析(异步读取) + if (keys.length === 0 && vertexFileList.length > 0) { + try { + const parsed = await Promise.all( + vertexFileList.map(async (item) => { + const fileObj = item.fileInstance; + if (!fileObj) return null; + const txt = await fileObj.text(); + return JSON.parse(txt); + }) + ); + keys = parsed.filter(Boolean); + } catch (err) { + showError(t('解析密钥文件失败: {{msg}}', { msg: err.message })); + return; + } + } + + // 创建模式必须上传密钥;编辑模式可选 + if (keys.length === 0) { + if (!isEdit) { + showInfo(t('请上传密钥文件!')); + return; + } else { + // 编辑模式且未上传新密钥,不修改 key + delete localInputs.key; + } } else { - localInputs.key = JSON.stringify(keys[0]); + // 有新密钥,则覆盖 + if (batch) { + localInputs.key = JSON.stringify(keys); + } else { + localInputs.key = JSON.stringify(keys[0]); + } } } } @@ -646,23 +673,33 @@ const EditChannel = (props) => { if (!checked) { setMultiToSingle(false); setMultiKeyMode('random'); + } else { + // 批量模式下禁用手动输入,并清空手动输入的内容 + setUseManualInput(false); + if (inputs.type === 41) { + // 清空手动输入的密钥内容 + if (formApiRef.current) { + formApiRef.current.setValue('key', ''); + } + handleInputChange('key', ''); + } } }} >{t('批量创建')} - {batch && ( - { - setMultiToSingle(prev => !prev); - setInputs(prev => { - const newInputs = { ...prev }; - if (!multiToSingle) { - newInputs.multi_key_mode = multiKeyMode; - } else { - delete newInputs.multi_key_mode; - } - return newInputs; - }); - }}>{t('密钥聚合模式')} - )} + {/*{batch && (*/} + {/* {*/} + {/* setMultiToSingle(prev => !prev);*/} + {/* setInputs(prev => {*/} + {/* const newInputs = { ...prev };*/} + {/* if (!multiToSingle) {*/} + {/* newInputs.multi_key_mode = multiKeyMode;*/} + {/* } else {*/} + {/* delete newInputs.multi_key_mode;*/} + {/* }*/} + {/* return newInputs;*/} + {/* });*/} + {/* }}>{t('密钥聚合模式')}*/} + {/*)}*/} ) : null; @@ -670,16 +707,68 @@ const EditChannel = (props) => { () => CHANNEL_OPTIONS.map((opt) => ({ ...opt, - label: ( - - {getChannelIcon(opt.value)} - {opt.label} - - ), + // 保持 label 为纯文本以支持搜索 + label: opt.label, })), [], ); + const renderChannelOption = (renderProps) => { + const { + disabled, + selected, + label, + value, + focused, + className, + style, + onMouseEnter, + onClick, + ...rest + } = renderProps; + + const searchWords = channelSearchValue ? [channelSearchValue] : []; + + // 构建样式类名 + const optionClassName = [ + 'flex items-center gap-3 px-3 py-2 transition-all duration-200 rounded-lg mx-2 my-1', + focused && 'bg-blue-50 shadow-sm', + selected && 'bg-blue-100 text-blue-700 shadow-lg ring-2 ring-blue-200 ring-opacity-50', + disabled && 'opacity-50 cursor-not-allowed', + !disabled && 'hover:bg-gray-50 hover:shadow-md cursor-pointer', + className + ].filter(Boolean).join(' '); + + return ( +
!disabled && onClick()} + onMouseEnter={e => onMouseEnter()} + > +
+
+ {getChannelIcon(value)} +
+
+ +
+ {selected && ( +
+ + + +
+ )} +
+
+ ); + }; + return ( <> { style={{ width: '100%' }} filter searchPosition='dropdown' + onSearch={(value) => setChannelSearchValue(value)} + renderOptionItem={renderChannelOption} onChange={(value) => handleInputChange('type', value)} /> @@ -797,22 +888,91 @@ const EditChannel = (props) => { ) : ( <> {inputs.type === 41 ? ( - } - dragMainText={t('点击上传文件或拖拽文件到这里')} - dragSubText={t('仅支持 JSON 文件')} - style={{ marginTop: 10 }} - uploadTrigger='custom' - beforeUpload={() => false} - onChange={handleVertexUploadChange} - fileList={vertexFileList} - rules={isEdit ? [] : [{ required: true, message: t('请上传密钥文件') }]} - extraText={batchExtra} - /> + <> + {!batch && ( +
+ {t('密钥输入方式')} + + + + +
+ )} + + {batch && ( + + )} + + {useManualInput && !batch ? ( + handleInputChange('key', value)} + extraText={ +
+ + {t('请输入完整的 JSON 格式密钥内容')} + + {batchExtra} +
+ } + autosize + showClear + /> + ) : ( + } + dragMainText={t('点击上传文件或拖拽文件到这里')} + dragSubText={t('仅支持 JSON 文件')} + style={{ marginTop: 10 }} + uploadTrigger='custom' + beforeUpload={() => false} + onChange={handleVertexUploadChange} + fileList={vertexFileList} + rules={isEdit ? [] : [{ required: true, message: t('请上传密钥文件') }]} + extraText={batchExtra} + /> + )} + ) : (