diff --git a/web/src/pages/Setting/Chat/SettingsChats.jsx b/web/src/pages/Setting/Chat/SettingsChats.jsx index dec27bea..368a66f5 100644 --- a/web/src/pages/Setting/Chat/SettingsChats.jsx +++ b/web/src/pages/Setting/Chat/SettingsChats.jsx @@ -18,7 +18,25 @@ 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, +} from '@douyinfe/semi-icons'; import { compareObjects, API, @@ -37,6 +55,52 @@ export default function SettingsChats(props) { }); const refForm = useRef(); const [inputsRow, setInputsRow] = useState(inputs); + const [editMode, setEditMode] = useState('json'); + 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 { @@ -104,15 +168,145 @@ export default function SettingsChats(props) { setInputs(currentInputs); setInputsRow(structuredClone(currentInputs)); 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) => { + 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 columns = [ + { + title: t('聊天应用名称'), + dataIndex: 'name', + key: 'name', + render: (text) => text || t('未命名'), + }, + { + title: t('URL链接'), + dataIndex: 'url', + key: 'url', + render: (text) => ( +
{text}
+ ), + }, + { + title: t('操作'), + key: 'action', + render: (_, record) => ( + + + + + ), + }, + ]; + return ( -
(refForm.current = formAPI)} - style={{ marginBottom: 15 }} - > + - { - return verifyJSON(value); - }, - message: t('不是合法的 JSON 字符串'), - }, - ]} - onChange={(value) => - setInputs({ - ...inputs, - Chats: value, - }) - } - /> + + + +
+ + {t('编辑模式')}: + + { + const newMode = e.target.value; + setEditMode(newMode); + + // 确保模式切换时数据正确同步 + setTimeout(() => { + if (newMode === 'json' && refForm.current) { + refForm.current.setValues(inputs); + } + }, 100); + }} + > + {t('可视化编辑')} + {t('JSON编辑')} + +
+ + {editMode === 'visual' ? ( +
+ + + } + placeholder={t('搜索聊天应用名称')} + value={searchText} + onChange={(value) => setSearchText(value)} + style={{ width: 250 }} + showClear + /> + + + + t('共 {{total}} 项,当前显示 {{start}}-{{end}} 项', { + total, + start: range[0], + end: range[1], + }), + }} + /> + + ) : ( + (refForm.current = formAPI)} + > + { + return verifyJSON(value); + }, + message: t('不是合法的 JSON 字符串'), + }, + ]} + onChange={(value) => + setInputs({ + ...inputs, + Chats: value, + }) + } + /> + + )} - - - + + + + + + +
(modalFormRef.current = api)}> + + + + +
); }