diff --git a/web/src/pages/Setting/Chat/SettingsChats.jsx b/web/src/pages/Setting/Chat/SettingsChats.jsx index dec27bea..01591c78 100644 --- a/web/src/pages/Setting/Chat/SettingsChats.jsx +++ b/web/src/pages/Setting/Chat/SettingsChats.jsx @@ -18,7 +18,26 @@ For commercial licensing, please contact support@quantumnous.com */ import React, { useEffect, useState, useRef } from 'react'; -import { Banner, Button, Form, Space, Spin } from '@douyinfe/semi-ui'; +import { + Banner, + Button, + Form, + Space, + Spin, + RadioGroup, + Radio, + Table, + Modal, + Input, + Divider, +} from '@douyinfe/semi-ui'; +import { + IconPlus, + IconEdit, + IconDelete, + IconSearch, + IconSaveStroked, +} from '@douyinfe/semi-icons'; import { compareObjects, API, @@ -37,6 +56,52 @@ export default function SettingsChats(props) { }); const refForm = useRef(); const [inputsRow, setInputsRow] = useState(inputs); + const [editMode, setEditMode] = useState('visual'); + const [chatConfigs, setChatConfigs] = useState([]); + const [modalVisible, setModalVisible] = useState(false); + const [editingConfig, setEditingConfig] = useState(null); + const [isEdit, setIsEdit] = useState(false); + const [searchText, setSearchText] = useState(''); + const modalFormRef = useRef(); + + const jsonToConfigs = (jsonString) => { + try { + const configs = JSON.parse(jsonString); + return Array.isArray(configs) + ? configs.map((config, index) => ({ + id: index, + name: Object.keys(config)[0] || '', + url: Object.values(config)[0] || '', + })) + : []; + } catch (error) { + console.error('JSON parse error:', error); + return []; + } + }; + + const configsToJson = (configs) => { + const jsonArray = configs.map((config) => ({ + [config.name]: config.url, + })); + return JSON.stringify(jsonArray, null, 2); + }; + + const syncJsonToConfigs = () => { + const configs = jsonToConfigs(inputs.Chats); + setChatConfigs(configs); + }; + + const syncConfigsToJson = (configs) => { + const jsonString = configsToJson(configs); + setInputs((prev) => ({ + ...prev, + Chats: jsonString, + })); + if (refForm.current && editMode === 'json') { + refForm.current.setValues({ Chats: jsonString }); + } + }; async function onSubmit() { try { @@ -103,16 +168,184 @@ export default function SettingsChats(props) { } setInputs(currentInputs); setInputsRow(structuredClone(currentInputs)); - refForm.current.setValues(currentInputs); + if (refForm.current) { + refForm.current.setValues(currentInputs); + } + + // 同步到可视化配置 + const configs = jsonToConfigs(currentInputs.Chats || '[]'); + setChatConfigs(configs); }, [props.options]); + useEffect(() => { + if (editMode === 'visual') { + syncJsonToConfigs(); + } + }, [inputs.Chats, editMode]); + + useEffect(() => { + if (refForm.current && editMode === 'json') { + refForm.current.setValues(inputs); + } + }, [editMode, inputs]); + + const handleAddConfig = () => { + setEditingConfig({ name: '', url: '' }); + setIsEdit(false); + setModalVisible(true); + setTimeout(() => { + if (modalFormRef.current) { + modalFormRef.current.setValues({ name: '', url: '' }); + } + }, 100); + }; + + const handleEditConfig = (config) => { + setEditingConfig({ ...config }); + setIsEdit(true); + setModalVisible(true); + setTimeout(() => { + if (modalFormRef.current) { + modalFormRef.current.setValues(config); + } + }, 100); + }; + + const handleDeleteConfig = (id) => { + const newConfigs = chatConfigs.filter((config) => config.id !== id); + setChatConfigs(newConfigs); + syncConfigsToJson(newConfigs); + showSuccess(t('删除成功')); + }; + + const handleModalOk = () => { + if (modalFormRef.current) { + modalFormRef.current + .validate() + .then((values) => { + // 检查名称是否重复 + const isDuplicate = chatConfigs.some( + (config) => + config.name === values.name && + (!isEdit || config.id !== editingConfig.id) + ); + + if (isDuplicate) { + showError(t('聊天应用名称已存在,请使用其他名称')); + return; + } + + if (isEdit) { + const newConfigs = chatConfigs.map((config) => + config.id === editingConfig.id + ? { ...editingConfig, name: values.name, url: values.url } + : config, + ); + setChatConfigs(newConfigs); + syncConfigsToJson(newConfigs); + } else { + const maxId = + chatConfigs.length > 0 + ? Math.max(...chatConfigs.map((c) => c.id)) + : -1; + const newConfig = { + id: maxId + 1, + name: values.name, + url: values.url, + }; + const newConfigs = [...chatConfigs, newConfig]; + setChatConfigs(newConfigs); + syncConfigsToJson(newConfigs); + } + setModalVisible(false); + setEditingConfig(null); + showSuccess(isEdit ? t('编辑成功') : t('添加成功')); + }) + .catch((error) => { + console.error('Modal form validation error:', error); + }); + } + }; + + const handleModalCancel = () => { + setModalVisible(false); + setEditingConfig(null); + }; + + const filteredConfigs = chatConfigs.filter( + (config) => + !searchText || + config.name.toLowerCase().includes(searchText.toLowerCase()), + ); + + const highlightKeywords = (text) => { + if (!text) return text; + + const parts = text.split(/(\{address\}|\{key\})/g); + return parts.map((part, index) => { + if (part === '{address}') { + return ( + + {part} + + ); + } else if (part === '{key}') { + return ( + + {part} + + ); + } + return part; + }); + }; + + const columns = [ + { + title: t('聊天应用名称'), + dataIndex: 'name', + key: 'name', + render: (text) => text || t('未命名'), + }, + { + title: t('URL链接'), + dataIndex: 'url', + key: 'url', + render: (text) => ( +