diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index a5ac694f..de5d9bce 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1143,8 +1143,8 @@ "默认测试模型": "Default Test Model", "不填则为模型列表第一个": "First model in list if empty", "是否自动禁用(仅当自动禁用开启时有效),关闭后不会自动禁用该渠道": "Auto-disable (only effective when auto-disable is enabled). When turned off, this channel will not be automatically disabled", - "状态码复写(仅影响本地判断,不修改返回到上游的状态码)": "Status Code Override (only affects local judgment, does not modify status code returned upstream)", - "此项可选,用于复写返回的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:": "Optional, used to override returned status codes, e.g. rewriting Claude channel's 400 error to 500 (for retry). Do not abuse this feature. Example:", + "状态码复写": "Status Code Override", + "此项可选,用于复写返回的状态码,仅影响本地判断,不修改返回到上游的状态码,比如将claude渠道的400错误复写为500(用于重试),请勿滥用该功能,例如:": "Optional, used to override returned status codes, only affects local judgment, does not modify status code returned upstream, e.g. rewriting Claude channel's 400 error to 500 (for retry). Do not abuse this feature. Example:", "渠道标签": "Channel Tag", "渠道优先级": "Channel Priority", "渠道权重": "Channel Weight", diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index ad67f90c..043a3f7c 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -1,4 +1,4 @@ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { @@ -15,10 +15,7 @@ import { Space, Spin, Button, - Input, Typography, - Select, - TextArea, Checkbox, Banner, Modal, @@ -26,6 +23,9 @@ import { Card, Tag, Avatar, + Form, + Row, + Col, } from '@douyinfe/semi-ui'; import { getChannelModels, copy } from '../../helpers'; import { @@ -113,7 +113,12 @@ const EditChannel = (props) => { const [customModel, setCustomModel] = useState(''); const [modalImageUrl, setModalImageUrl] = useState(''); const [isModalOpenurl, setIsModalOpenurl] = useState(false); + const formApiRef = useRef(null); + const getInitValues = () => ({ ...originInputs }); const handleInputChange = (name, value) => { + if (formApiRef.current) { + formApiRef.current.setValue(name, value); + } if (name === 'models' && Array.isArray(value)) { value = Array.from(new Set(value.map((m) => (m || '').trim()))); } @@ -205,6 +210,9 @@ const EditChannel = (props) => { ); } setInputs(data); + if (formApiRef.current) { + formApiRef.current.setValues(data); + } if (data.auto_ban === 0) { setAutoBan(false); } else { @@ -338,30 +346,51 @@ const EditChannel = (props) => { useEffect(() => { fetchModels().then(); fetchGroups().then(); - if (isEdit) { - loadChannel().then(() => { }); - } else { + if (!isEdit) { setInputs(originInputs); + if (formApiRef.current) { + formApiRef.current.setValues(originInputs); + } let localModels = getChannelModels(inputs.type); setBasicModels(localModels); setInputs((inputs) => ({ ...inputs, models: localModels })); } }, [props.editingChannel.id]); + useEffect(() => { + if (formApiRef.current) { + formApiRef.current.setValues(inputs); + } + }, [inputs]); + + useEffect(() => { + if (props.visible) { + if (isEdit) { + loadChannel(); + } else { + formApiRef.current?.setValues(getInitValues()); + } + } else { + formApiRef.current?.reset(); + } + }, [props.visible, channelId]); + const submit = async () => { - if (!isEdit && (inputs.name === '' || inputs.key === '')) { + const formValues = formApiRef.current ? formApiRef.current.getValues() : {}; + let localInputs = { ...formValues }; + + if (!isEdit && (!localInputs.name || !localInputs.key)) { showInfo(t('请填写渠道名称和渠道密钥!')); return; } - if (inputs.models.length === 0) { + if (!Array.isArray(localInputs.models) || localInputs.models.length === 0) { showInfo(t('请至少选择一个模型!')); return; } - if (inputs.model_mapping !== '' && !verifyJSON(inputs.model_mapping)) { + if (localInputs.model_mapping && localInputs.model_mapping !== '' && !verifyJSON(localInputs.model_mapping)) { showInfo(t('模型映射必须是合法的 JSON 格式!')); return; } - let localInputs = { ...inputs }; if (localInputs.base_url && localInputs.base_url.endsWith('/')) { localInputs.base_url = localInputs.base_url.slice( 0, @@ -372,14 +401,9 @@ const EditChannel = (props) => { localInputs.other = 'v2.1'; } let res; - if (!Array.isArray(localInputs.models)) { - showError(t('提交失败,请勿重复提交!')); - handleCancel(); - return; - } - localInputs.auto_ban = autoBan ? 1 : 0; + localInputs.auto_ban = localInputs.auto_ban ? 1 : 0; localInputs.models = localInputs.models.join(','); - localInputs.group = localInputs.groups.join(','); + localInputs.group = (localInputs.groups || []).join(','); if (isEdit) { res = await API.put(`/api/channel/`, { ...localInputs, @@ -439,6 +463,11 @@ const EditChannel = (props) => { } }; + const batchAllowed = !isEdit && inputs.type !== 41; + const batchExtra = batchAllowed ? ( + setBatch(!batch)}>{t('批量创建')} + ) : null; + return ( <> {