18586361686
2025-05-18 8efd09723dabfaa86c016aee8a0ab1ce3b3e67eb
feat: 插件输入参数和输出参数支持子节点
2个文件已修改
1个文件已添加
1214 ■■■■■ 已修改文件
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/service/impl/AiPluginToolServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/plugin/PluginInputAndOutputData.tsx 637 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-ui-react/src/pages/ai/plugin/PluginToolEdit.tsx 562 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aiflowy-modules/aiflowy-module-ai/src/main/java/tech/aiflowy/ai/service/impl/AiPluginToolServiceImpl.java
@@ -72,21 +72,6 @@
    @Override
    public Result updatePlugin(AiPluginTool aiPluginTool) {
        String inputData = null;
        String outputData = null;
        if (!StrUtil.isEmpty(aiPluginTool.getInputData())) {
            inputData = switchParams(aiPluginTool.getInputData());
        }
        if (!StrUtil.isEmpty(inputData)) {
            aiPluginTool.setInputData(inputData);
        }
        if (!StrUtil.isEmpty(aiPluginTool.getOutputData())) {
            outputData = switchParams(aiPluginTool.getOutputData());
        }
        if (!StrUtil.isEmpty(outputData)) {
            aiPluginTool.setOutputData(outputData);
        }
        int update = aiPluginToolMapper.update(aiPluginTool);
        if (update <= 0) {
            return Result.fail(1, "修改失败");
aiflowy-ui-react/src/pages/ai/plugin/PluginInputAndOutputData.tsx
New file
@@ -0,0 +1,637 @@
import React, {forwardRef, useEffect, useImperativeHandle, useState} from 'react';
import {Table, Input, Select, Button, Space, Form, Switch, Tooltip} from 'antd';
import type {ExpandableConfig} from 'antd/es/table/interface';
import {DeleteOutlined, PlusOutlined} from '@ant-design/icons';
export interface TreeTableNode {
    key: string;
    name: string;
    description: string;
    method?: 'Query' | 'Body' | 'Path' | 'Header';
    required?: boolean;
    defaultValue?: string;
    enabled?: boolean;
    type?: any;
    children?: TreeTableNode[];
}
interface PluginInputDataProps {
    value?: TreeTableNode[],
    onChange?: (value: TreeTableNode[]) => void,
    onSubmit?: (value: TreeTableNode[]) => void,
    editable?: boolean,
    isEditOutput?: boolean,
    submitParams?: () => void,
}
export interface PluginInputDataRef {
    handleSubmitParams: () => void;
}
const PluginInputAndOutputData: React.ForwardRefRenderFunction<PluginInputDataRef, PluginInputDataProps> = ({
                                                             value = [],
                                                             onChange,
                                                             onSubmit,
                                                             editable = false,
                                                             isEditOutput = false
                                                         }, ref) => {
    const [data, setData] = useState<TreeTableNode[]>(value);
    useEffect(() => {
        if (value) {
            setData([...value]); // 或者 cloneDeep(value)
        }
    }, [value]);
    const [expandedKeys, setExpandedKeys] = useState<React.Key[]>(['1']);
    const [errors, setErrors] = useState<Record<string, Partial<Record<keyof TreeTableNode, string>>>>({});
    const updateData = (newData: TreeTableNode[]) => {
        setData(newData);
        if (onChange) {
            onChange(newData);
        }
    };
    const handleSubmitParams = () => {
        if (!validateFields()) return;
        if (onSubmit) {
            onSubmit(data); // 这里会传给父组件
        }
    };
    // 暴露方法给父组件
    useImperativeHandle(ref, () => ({
        handleSubmitParams,
    }));
    const addNewRootNode = () => {
        if (!editable) return;
        const newKey = `${Date.now()}`;
        const newNode: TreeTableNode = {
            key: newKey,
            name: '',
            description: '',
            enabled: true,
            type: 'String',
            // 动态添加 method 字段
            ...(isEditOutput ? {} : { method: 'Query', defaultValue: '', required: false, }),
        };
        const newData = [...data, newNode];
        updateData(newData);
    };
    const onExpand = (expanded: boolean, record: TreeTableNode) => {
        const keys = expanded
            ? [...expandedKeys, record.key]
            : expandedKeys.filter(key => key !== record.key);
        setExpandedKeys(keys);
    };
    const handleAddChild = (parentKey: any) => {
        if (!editable || !parentKey) return;
        const newChild: TreeTableNode = {
            key: `${parentKey}-${Date.now()}`,
            name: '',
            description: '',
            required: false,
            enabled: true,
            type: 'String',
            ...(isEditOutput ? {} : { method: 'Query', defaultValue: '', }),
        };
        const addChildToNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
            nodes.map(node => {
                if (node.key === parentKey) {
                    return {
                        ...node,
                        children: [...(node.children || []), newChild],
                    };
                }
                if (node.children) {
                    return {
                        ...node,
                        children: addChildToNode(node.children),
                    };
                }
                return node;
            });
        const newData = addChildToNode(data);
        updateData(newData);
        if (!expandedKeys.includes(parentKey)) {
            setExpandedKeys([...expandedKeys, parentKey]);
        }
    };
    const deleteNode = (key: string) => {
        if (!editable) return;
        const removeNodeRecursively = (nodes: TreeTableNode[]): TreeTableNode[] =>
            nodes.filter(node => {
                if (node.key === key) return false;
                if (node.children)
                    node.children = removeNodeRecursively(node.children);
                return true;
            });
        const newData = removeNodeRecursively(data);
        updateData(newData);
    };
    const validateFields = (): boolean => {
        const newErrors: Record<string, Partial<Record<keyof TreeTableNode, string>>> = {};
        let isValid = true;
        const checkNode = (node: TreeTableNode): boolean => {
            const {name, description, method, type} = node;
            const nodeErrors: Partial<Record<keyof TreeTableNode, string>> = {};
            if (!name?.trim()) {
                nodeErrors.name = '参数名称不能为空';
                isValid = false;
            }
            if (!description?.trim()) {
                nodeErrors.description = '参数描述不能为空';
                isValid = false;
            }
            if ((isRootNode(node) && !method) && !isEditOutput) {
                nodeErrors.method = '传入方法不能为空';
                isValid = false;
            }
            if (!type) {
                nodeErrors.type = '参数类型不能为空';
                isValid = false;
            }
            if (Object.keys(nodeErrors).length > 0) {
                newErrors[node.key] = nodeErrors;
            }
            if (node.children) {
                node.children.forEach(child => {
                    if (!checkNode(child)) isValid = false;
                });
            }
            return isValid;
        };
        data.forEach(node => {
            if (!checkNode(node)) isValid = false;
        });
        setErrors(newErrors);
        return isValid;
    };
    const isRootNode = (record: TreeTableNode) => {
        return !record.key.includes('-');
    };
    // @ts-ignore
    const columns = [
        {
            title: <span>参数名称<span style={{color: 'red'}}>*</span></span>,
            dataIndex: 'name',
            key: 'name',
            width: '20%',
            onCell: () => ({style: {paddingLeft: 2}}),
            // @ts-ignore
            render: (text: string, record: TreeTableNode) => {
                const fieldError = errors[record.key]?.name;
                const level = String(record.key).split('-').length - 1;
                const indentSize = 2;
                if (!editable) {
                    return (
                        <div style={{paddingLeft: level * indentSize}}>
                            {record.name || ''}
                        </div>
                    );
                }
                const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
                    const value = e.target.value;
                    const updateNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                        nodes.map(node => {
                            if (node.key === record.key) {
                                return {...node, name: value};
                            }
                            if (node.children) {
                                return {...node, children: updateNode(node.children)};
                            }
                            return node;
                        });
                    const newData = updateNode(data);
                    updateData(newData);
                };
                // if (record.type ==== 'Array'){
                //
                // }
                return (
                    <div style={{
                        display: 'flex',
                        position: 'relative',
                        flexDirection: "column",
                        justifyContent: "flex-start"
                    }}>
                        <div style={{display: 'flex', alignItems: "center"}}>
                            <div style={{width: level * indentSize}}></div>
                            <Input
                                variant="filled"
                                value={record.name || ''}
                                onChange={handleChange}
                                size="middle"
                                disabled={record.name === 'arrayItem'}
                                style={{flex: 1}}
                            />
                        </div>
                        {fieldError && <div style={{
                            color: 'red',
                            fontSize: '12px',
                            top: '90%',
                            marginLeft: level * indentSize,
                            position: 'absolute',
                        }}>{fieldError}{level}</div>}
                    </div>
                );
            },
        },
        {
            title: <span>参数描述<span style={{color: 'red'}}>*</span></span>,
            dataIndex: 'description',
            key: 'description',
            width: '20%',
            // @ts-ignore
            render: (text: string, record: TreeTableNode) => {
                const fieldError = errors[record.key]?.description;
                if (!editable) {
                    return <span>{record.description || ''}</span>;
                }
                const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
                    const value = e.target.value;
                    const updateNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                        nodes.map(node => {
                            if (node.key === record.key) {
                                return {...node, description: value};
                            }
                            if (node.children) {
                                return {...node, children: updateNode(node.children)};
                            }
                            return node;
                        });
                    const newData = updateNode(data);
                    updateData(newData);
                };
                return (
                    <div style={{position: 'relative'}}>
                        <Input
                            variant="filled"
                            size="middle"
                            value={record.description || ''}
                            onChange={handleChange}
                            disabled={!editable}
                        />
                        {fieldError && <div style={{
                            color: 'red',
                            fontSize: '12px',
                            position: 'absolute',
                            top: '90%'
                        }}>{fieldError}</div>}
                    </div>
                );
            },
        },
        {
            title: <span>参数类型<span style={{color: 'red'}}>*</span></span>,
            dataIndex: 'type',
            key: 'type',
            width: '10%',
            // @ts-ignore
            render: (text: string, record: TreeTableNode) => {
                const fieldError = errors[record.key]?.type;
                const handleChange = (value: 'String' | 'Number' | 'Object' | 'Boolean' | 'Array' | 'Array[String]' |
                    'Array[Number]' | 'Array[Boolean]' | 'Array[Integer]' | 'Array[Object]') => {
                    if (!editable) return;
                    const updateNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                        nodes.map(node => {
                            if (node.key === record.key) {
                                // 如果是 String 或 Number,移除 children
                                if (value === 'String' || value === 'Number' || value === 'Boolean' || value === 'Array[String]'
                                    || value === 'Array[Number]' || value === 'Array[Boolean]' || value === 'Array[Integer]' || 'Array[Object]') {
                                    return {
                                        ...node,
                                        type: value,
                                        children: undefined, // 移除 children
                                    };
                                }
                                // 如果是 Object 或 Array,保留或初始化 children
                                return {
                                    ...node,
                                    type: value,
                                    children: node.children || [], // 保留现有 children 或初始化为空数组
                                };
                            }
                            if (node.children) {
                                return {
                                    ...node,
                                    children: updateNode(node.children),
                                };
                            }
                            return node;
                        });
                    const newData = updateNode(data);
                    updateData(newData);
                    // 如果是 Object 或 Array,添加默认子节点并展开
                    if (value === 'Object' || value === 'Array' || value === 'Array[Object]') {
                        const newChild: TreeTableNode = {
                            key: `${record.key}-${Date.now()}`, // 唯一 key
                            name: value === 'Array' ? 'arrayItem' : '', // Array 类型子节点固定名称为 arrayItem
                            description: '',
                            enabled: true,
                            ...(isEditOutput ? {} : { method: 'Query', defaultValue: '', required: false, }),
                            type: value === 'Array' ? 'Array[String]' : 'String', // Array 的子节点默认类型为 Array[String]
                        };
                        const addChildToNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                            nodes.map(node => {
                                if (node.key === record.key) {
                                    return {
                                        ...node,
                                        children: [newChild],
                                    };
                                }
                                if (node.children) {
                                    return {
                                        ...node,
                                        children: addChildToNode(node.children),
                                    };
                                }
                                return node;
                            });
                        const finalData = addChildToNode(newData);
                        updateData(finalData);
                        // 自动展开父节点
                        if (!expandedKeys.includes(record.key)) {
                            setExpandedKeys([...expandedKeys, record.key]);
                        }
                    }
                };
                if (!editable) {
                    return <span>{record.type || ''}</span>;
                }
                return (
                    <Form.Item style={{margin: 0}}>
                        <Select
                            value={record.type || 'String'}
                            variant="filled"
                            onChange={handleChange}
                            options={record.name === 'arrayItem' ?
                                [
                                    {label: 'Array[String]', value: 'Array[String]'},
                                    {label: 'Array[Number]', value: 'Array[Number]'},
                                    {label: 'Array[Boolean]', value: 'Array[Boolean]'},
                                    {label: 'Array[Object]', value: 'Array[Object]'}
                                ]
                                :
                                [
                                    {label: 'String', value: 'String'},
                                    {label: 'Boolean', value: 'Boolean'},
                                    {label: 'Number', value: 'Number'},
                                    {label: 'Object', value: 'Object'},
                                    {label: 'Array', value: 'Array'},
                                ]
                            }
                            size="middle"
                            disabled={!editable}
                        />
                        {fieldError && <div style={{color: 'red', fontSize: '12px'}}>{fieldError}</div>}
                    </Form.Item>
                );
            },
        },
        ...(!isEditOutput
            ? [
                {
                    title: <span>传入方法<span style={{color: 'red'}}>*</span></span>,
                    dataIndex: 'method',
                    key: 'method',
                    width: '10%',
                    // @ts-ignore
                    render: (text: string, record: TreeTableNode) => {
                        const fieldError = errors[record.key]?.method;
                        // if (!isRootNode(record)) {
                        //     return <span>{record.method || ''}</span>;
                        // }
                        if (record.name === 'arrayItem') {
                            return <span>{''}</span>;
                        }
                        const handleChange = (value: 'Query' | 'Body' | 'Path' | 'Header') => {
                            if (!editable) return;
                            const updateNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                                nodes.map(node => {
                                    if (node.key === record.key) {
                                        return {...node, method: value};
                                    }
                                    if (node.children) {
                                        return {...node, children: updateNode(node.children)};
                                    }
                                    return node;
                                });
                            const newData = updateNode(data);
                            updateData(newData);
                        };
                        if (!editable) {
                            return <span>{record.method || ''}</span>;
                        }
                        return (
                            <Form.Item style={{margin: 0}}>
                                <Select
                                    value={record.method || 'Query'}
                                    onChange={handleChange}
                                    variant="filled"
                                    options={[
                                        {label: 'Query', value: 'Query'},
                                        {label: 'Body', value: 'Body'},
                                        {label: 'Path', value: 'Path'},
                                        {label: 'Header', value: 'Header'},
                                    ]}
                                    size="middle"
                                    disabled={!editable}
                                />
                                {fieldError && <div style={{color: 'red', fontSize: '12px'}}>{fieldError}</div>}
                            </Form.Item>
                        );
                    },
                },
                {
                    title: '是否必填',
                    dataIndex: 'required',
                    key: 'required',
                    width: '8%',
                    // @ts-ignore
                    render: (text: boolean, record: TreeTableNode) => {
                        const handleChange = (checked: boolean) => {
                            if (!editable) return;
                            const updateNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                                nodes.map(node => {
                                    if (node.key === record.key) {
                                        return { ...node, required: checked };
                                    }
                                    if (node.children) {
                                        return { ...node, children: updateNode(node.children) };
                                    }
                                    return node;
                                });
                            const newData = updateNode(data);
                            updateData(newData);
                        };
                        return <Switch checked={record.required} onChange={handleChange} disabled={!editable} />;
                    },
                },
                {
                    title: '默认值',
                    dataIndex: 'defaultValue',
                    key: 'defaultValue',
                    width: '12%',
                    // @ts-ignore
                    render: (text: string, record: TreeTableNode) => {
                        if (record.type === 'Object') {
                            return <span></span>;
                        }
                        const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
                            const value = e.target.value;
                            const updateNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                                nodes.map(node => {
                                    if (node.key === record.key) {
                                        return {...node, defaultValue: value};
                                    }
                                    if (node.children) {
                                        return {...node, children: updateNode(node.children)};
                                    }
                                    return node;
                                });
                            const newData = updateNode(data);
                            updateData(newData);
                        };
                        if (!editable) {
                            return <span>{record.defaultValue || ''}</span>;
                        }
                        return (
                            <Input
                                size="middle"
                                variant="filled"
                                value={record.defaultValue || ''}
                                onChange={handleChange}
                                disabled={!editable}
                            />
                        );
                    },
                },
            ]
            : []),
        {
            title: '启用状态',
            dataIndex: 'enabled',
            key: 'enabled',
            width: '8%',
            // @ts-ignore
            render: (text: boolean, record: TreeTableNode) => {
                const handleChange = (checked: boolean) => {
                    if (!editable) return;
                    const updateNode = (nodes: TreeTableNode[]): TreeTableNode[] =>
                        nodes.map(node => {
                            if (node.key === record.key) {
                                return {...node, enabled: checked};
                            }
                            if (node.children) {
                                return {...node, children: updateNode(node.children)};
                            }
                            return node;
                        });
                    const newData = updateNode(data);
                    updateData(newData);
                };
                return (
                    <Switch
                        checked={record.enabled}
                        onChange={handleChange}
                        disabled={!editable}
                    />
                );
            },
        },
        ...(editable
            ? [
                {
                    title: '操作',
                    key: 'action',
                    width: '12%',
                    render: (_: any, record: TreeTableNode) => (
                        <Space size="middle">
                            {(record.type === 'Object' || record.type === 'Array[Object]') && (
                                <Tooltip title="添加子节点" placement="top">
                                    <Button
                                        type="link"
                                        onClick={() => handleAddChild(record.key)}
                                        icon={<PlusOutlined/>}
                                        size="small"
                                    />
                                </Tooltip>
                            )}
                            <Button
                                type="link"
                                danger
                                icon={<DeleteOutlined/>}
                                onClick={() => deleteNode(record.key)}
                                size="small"
                            />
                        </Space>
                    ),
                },
            ]
            : []),
    ];
    const expandable: ExpandableConfig<TreeTableNode> = {
        expandedRowKeys: expandedKeys,
        onExpand: onExpand,
        rowExpandable: (record) => !!record.children && record.children.length > 0,
    };
    return (
        <>
            <Table
                columns={columns}
                expandable={expandable}
                dataSource={data}
                rowKey="key"
                pagination={false}
                size="middle"
                bordered
            />
            {editable && (
                <div style={{marginTop: 16, textAlign: 'left'}}>
                    {/*<Button type="primary" onClick={handleSubmitParams}>*/}
                    {/*    提交参数*/}
                    {/*</Button>*/}
                    <Button type="default" onClick={addNewRootNode}>
                        新增参数
                    </Button>
                </div>
            )}
        </>
    );
};
export default forwardRef(PluginInputAndOutputData);
aiflowy-ui-react/src/pages/ai/plugin/PluginToolEdit.tsx
@@ -1,37 +1,16 @@
import React, {useEffect, useState} from 'react';
import React, {useEffect, useRef, useState} from 'react';
import {useLayout} from "../../../hooks/useLayout.tsx";
import {useLocation, useNavigate} from "react-router-dom";
import {Button, Collapse, Form, Input, message, Select, Space, Spin, Switch, Table, Tooltip} from "antd";
import {Button, Collapse, Form, Input, message, Select, Spin} from "antd";
import {usePost, usePostManual} from "../../../hooks/useApis.ts";
import './less/pluginToolEdit.less'
import {
    ArrowLeftOutlined,
    DeleteOutlined,
    EditOutlined,
    PlusOutlined,
    QuestionCircleOutlined
} from "@ant-design/icons";
import TextArea from "antd/es/input/TextArea";
import {useBreadcrumbRightEl} from "../../../hooks/useBreadcrumbRightEl.tsx";
interface inputDataParameter {
    key: string;
    name: string;
    description: string;
    type: string;
    method: string;
    required: boolean;
    defaultValue: string;
    enabled: boolean;
}
interface outputDataParameter {
    key: string;
    name: string;
    description: string;
    type: string;
    required: boolean;
    enabled: boolean;
}
import PluginInputAndOutputData, {TreeTableNode} from "./PluginInputAndOutputData.tsx";
const PluginToolEdit: React.FC = () => {
@@ -50,11 +29,10 @@
    const { id, pluginTitle, pluginToolTitle } = location.state || {};
    const { result: pluginToolInfo, doPost: doPostSearch } = usePost('/api/v1/aiPluginTool/tool/search');
    const {  doPost: doPostUpdate } = usePostManual('/api/v1/aiPluginTool/tool/update')
    const pluginRef = useRef(null);
    const [showLoading, setShowLoading] = useState(true);
    const [isEditInput, setIsEditInput] = useState(false);
    const [isEditOutput, setIsEditOutput] = useState(false);
    const [formInput] = Form.useForm();
    const [formOutput] = Form.useForm();
    useBreadcrumbRightEl(
        <div style={{display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 16}}>
            <div>
@@ -109,350 +87,19 @@
    };
    const handleAdd = () => {
        const newData: inputDataParameter = {
            key: Date.now().toString(),
            name: '',
            description: '',
            type: 'String',
            method: 'Query',
            required: true,
            defaultValue: '',
            enabled: true,
        };
        setInputData([...inputData, newData]);
    };
    const handleAddOutputData = () => {
        const newData: outputDataParameter = {
            key: Date.now().toString(),
            name: '',
            description: '',
            type: 'String',
            required: true,
            enabled: true,
        };
        setOutputData([...outputData, newData]);
    };
    const handleDeleteInputData = (key: string) => {
        setInputData(inputData.filter((item) => item.key !== key));
    };
    const handleDeleteOutputData = (key: string) => {
        setOutputData(outputData.filter((item) => item.key !== key));
    };
    const [inputData, setInputData] = useState<inputDataParameter[]>([
    const [inputDataTree, setInputDataTree] = useState<TreeTableNode[]>([
    ]);
    const [outputData, setOutputData] = useState<outputDataParameter[]>([
    const [outputDataTree, setOutputDataTree] = useState<TreeTableNode[]>([
    ]);
    useEffect(() => {
        if (pluginToolInfo?.data?.data?.inputData){
            setInputData(JSON.parse(pluginToolInfo?.data?.data?.inputData));
            setInputDataTree(JSON.parse(pluginToolInfo?.data?.data?.inputData))
        }
        if(pluginToolInfo?.data?.data?.outputData){
            setOutputData(JSON.parse(pluginToolInfo?.data?.data?.outputData));
            setOutputDataTree(JSON.parse(pluginToolInfo?.data?.data?.outputData));
        }
    }, [pluginToolInfo]);
    const getColumnsOutput =()=>{
        const columnsOutput = [
            {
                title: (
                    <div style={{display: 'flex'}}>
                        <span>参数名称<span style={{ color: 'red', marginLeft: 4 }}>*</span></span>
                        <div  style={{marginLeft: 5}}>
                            <Tooltip title="当前工具返回的参数">
                                <QuestionCircleOutlined/>
                            </Tooltip>
                        </div>
                    </div>
                ),
                width: 150,
                dataIndex: 'name',
                key: 'name',
                render: (text: string, record: outputDataParameter) => {
                    return isEditOutput ? (
                        <Form.Item
                            className="tool-edit-item"
                            name={[record.key, 'name']}
                            initialValue={text}
                            rules={[{ required: true, message: '请输入参数名称' }]}
                        >
                            <Input placeholder="参数名称" />
                        </Form.Item>
                    ) : (
                        text
                    );
                },
            },
            {
                title: (
                    <div style={{display: 'flex'}}>
                        <span>参数描述<span style={{ color: 'red', marginLeft: 4 }}>*</span></span>
                        <div  style={{marginLeft: 5}}>
                            <Tooltip title="当前工具返回的参数描述">
                                <QuestionCircleOutlined/>
                            </Tooltip>
                        </div>
                    </div>
                ),
                dataIndex: 'description',
                key: 'description',
                render: (text: string, record: outputDataParameter) => {
                    return isEditOutput ? (
                        <Form.Item
                            className="tool-edit-item"
                            name={[record.key, 'description']}
                            initialValue={text}
                            rules={[{ required: true, message: '请输入参数描述' }]}
                        >
                            <Input placeholder="参数描述" />
                        </Form.Item>
                    ) : (
                        <Tooltip title={text}>
                            <div style={{
                                display: '-webkit-box',
                                WebkitLineClamp: 1,      // 限制显示行数
                                WebkitBoxOrient: 'vertical',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {text}
                            </div>
                        </Tooltip>
                    );
                },
            },
            {
                title: (
                    <span>参数类型<span style={{ color: 'red', marginLeft: 4 }}>*</span></span>
                ),
                width: 130,
                dataIndex: 'type',
                key: 'type',
                render: (text: string, record: outputDataParameter) => {
                    return isEditOutput ? (
                        <Form.Item name={[record.key, 'type']} initialValue={text} className="tool-edit-item">
                            <Select style={{ width: 120 }}>
                                <Select.Option value="String">String</Select.Option>
                                <Select.Option value="Number">Number</Select.Option>
                                <Select.Option value="Boolean">Boolean</Select.Option>
                                <Select.Option value="Object">Object</Select.Option>
                                <Select.Option value="Array">Array</Select.Option>
                            </Select>
                        </Form.Item>
                    ) : (
                        text
                    );
                },
            },
            {
                title: '开启',
                width: 80,
                dataIndex: 'enabled',
                key: 'enabled',
                render: (text: boolean, record: outputDataParameter) => {
                    return isEditOutput ? (
                        <Form.Item  name={[record.key, 'enabled']} initialValue={text} valuePropName="checked" className="tool-edit-item">
                            <Switch />
                        </Form.Item>
                    ) : (
                        <Switch checked={text} disabled />
                    );
                },
            },
            {
                title: '操作',
                key: 'operation',
                render: (_: any, record: outputDataParameter) => (
                    <Form.Item className="tool-edit-item">
                        <Button
                            type="text"
                            icon={<DeleteOutlined />}
                            onClick={() => handleDeleteOutputData(record.key)}
                        />
                    </Form.Item>
                ),
            },
        ];
        return columnsOutput.filter(col => col.key !== 'operation' || isEditOutput);
    }
    const getColumnsInput = ()=>{
        const columnsInput = [
            {
                title: (
                    <div style={{display: 'flex'}}>
                        <span>参数名称<span style={{ color: 'red', marginLeft: 4 }}>*</span></span>
                        <div  style={{marginLeft: 5}}>
                            <Tooltip title="当前工具请求的参数名称">
                                <QuestionCircleOutlined/>
                            </Tooltip>
                        </div>
                    </div>
                ),
                width: 150,
                dataIndex: 'name',
                key: 'name',
                render: (text: string, record: inputDataParameter) => {
                    return isEditInput ? (
                        <Form.Item
                            className="tool-edit-item"
                            name={[record.key, 'name']}
                            initialValue={text}
                            rules={[{ required: true, message: '请输入参数名称' }]}
                        >
                            <Input placeholder="参数名称" />
                        </Form.Item>
                    ) : (
                        text
                    );
                },
            },
            {
                title: (
                    <div style={{display: 'flex'}}>
                        <span>参数描述<span style={{ color: 'red', marginLeft: 4 }}>*</span></span>
                        <div  style={{marginLeft: 5}}>
                            <Tooltip title="请描述参数的功能,帮助用户/大模型更好的理解。">
                                <QuestionCircleOutlined/>
                            </Tooltip>
                        </div>
                    </div>
                ),
                dataIndex: 'description',
                key: 'description',
                render: (text: string, record: inputDataParameter) => {
                    return isEditInput ? (
                        <Form.Item
                            className="tool-edit-item"
                            name={[record.key, 'description']}
                            initialValue={text}
                            rules={[{ required: true, message: '请输入参数描述' }]}
                        >
                            <Input placeholder="参数描述" />
                        </Form.Item>
                    ) : (
                        <Tooltip title={text}>
                            <div style={{
                                display: '-webkit-box',
                                WebkitLineClamp: 1,      // 限制显示行数
                                WebkitBoxOrient: 'vertical',
                                overflow: 'hidden',
                                textOverflow: 'ellipsis',
                            }}>
                                {text}
                            </div>
                        </Tooltip>
                    );
                },
            },
            {
                title: (
                    <span>参数类型<span style={{ color: 'red', marginLeft: 4 }}>*</span></span>
                ),
                width: 130,
                dataIndex: 'type',
                key: 'type',
                render: (text: string, record: inputDataParameter) => {
                    return isEditInput ? (
                        <Form.Item name={[record.key, 'type']} initialValue={text} className="tool-edit-item">
                            <Select style={{ width: 120 }}>
                                <Select.Option value="String">String</Select.Option>
                                <Select.Option value="Number">Number</Select.Option>
                                <Select.Option value="Boolean">Boolean</Select.Option>
                                <Select.Option value="Object">Object</Select.Option>
                                <Select.Option value="Array">Array</Select.Option>
                            </Select>
                        </Form.Item>
                    ) : (
                        text
                    );
                },
            },
            {
                title: (
                    <span>传入方法<span style={{ color: 'red', marginLeft: 4 }}>*</span></span>
                ),
                width: 130,
                dataIndex: 'method',
                key: 'method',
                render: (text: string, record: inputDataParameter) => {
                    return isEditInput ? (
                        <Form.Item name={[record.key, 'method']} initialValue={text} className="tool-edit-item">
                            <Select style={{ width: 120 }}>
                                <Select.Option value="Query">Query</Select.Option>
                                <Select.Option value="Body">Body</Select.Option>
                                <Select.Option value="Header">Header</Select.Option>
                                <Select.Option value="Path">Path</Select.Option>
                            </Select>
                        </Form.Item>
                    ) : (
                        text
                    );
                },
            },
            {
                title: '是否必须',
                dataIndex: 'required',
                width: 90,
                key: 'required',
                render: (text: boolean, record: inputDataParameter) => {
                    return isEditInput ? (
                        <Form.Item name={[record.key, 'required']} initialValue={text} valuePropName="checked" className="tool-edit-item">
                            <Switch />
                        </Form.Item>
                    ) : (
                        <Switch checked={text} disabled />
                    );
                },
            },
            {
                title: '默认值',
                width: 100,
                dataIndex: 'defaultValue',
                key: 'defaultValue',
                render: (text: string, record: inputDataParameter) => {
                    return isEditInput ? (
                        <Form.Item name={[record.key, 'defaultValue']} initialValue={text} className="tool-edit-item">
                            <Input placeholder="默认值" size="small" />
                        </Form.Item>
                    ) : (
                        text
                    );
                },
            },
            {
                title: '开启',
                width: 80,
                dataIndex: 'enabled',
                key: 'enabled',
                render: (text: boolean, record: inputDataParameter) => {
                    return isEditInput ? (
                        <Form.Item name={[record.key, 'enabled']} initialValue={text} valuePropName="checked" className="tool-edit-item">
                            <Switch />
                        </Form.Item>
                    ) : (
                        <Switch checked={text} disabled />
                    );
                },
            },
            {
                title: '操作',
                key: 'operation',
                render: (_: any, record: inputDataParameter) => (
                    <Button
                        type="text"
                        icon={<DeleteOutlined />}
                        onClick={() => handleDeleteInputData(record.key)}
                    />
                ),
            },
        ];
        return columnsInput.filter(col => col.key !== 'operation' || isEditInput);
    }
    const editPluginTool = (index: string) => {
        // 可以在函数顶部添加条件判断
        if (!index) {
@@ -475,81 +122,52 @@
                            } else if (index === '3') {
                                setIsEditOutput(false);
                            }
                            doPostSearch({
                                data: {
                                    aiPluginToolId: id
                                }
                            }).then(res => {
                                setInputDataTree(JSON.parse(res?.data?.data?.data?.inputData))
                                setOutputDataTree(JSON.parse(res?.data?.data?.data?.outputData));
                            })
                        }}>取消</Button>
                        <Button
                            type="primary"
                            size="small"
                            onClick={(e) => {
                                e.stopPropagation();
                                formBasicInfo.validateFields().then((values) => {
                                    if (index === '1') {
                                        doPostUpdate({
                                            data: {
                                                id: values.id,
                                                name: values.name,
                                                description: values.description,
                                                basePath: values.basePath,
                                                requestMethod: values.requestMethod
                                            }
                                        }).then((r) => {
                                            if (r?.data?.errorCode === 0) {
                                                message.success('修改成功');
                                                setEditStates(prev => ({...prev, '1': false})); // 添加这行
                                                doPostSearch({
                                                    data: {
                                                        aiPluginToolId: id
                                                    }
                                                });
                                            }
                                        });
                                    }
                                    else if (index === '2') {
                                        formInput.validateFields().then(() => {
                                            const formData = formInput.getFieldsValue();
                                            doPostUpdate({
                                                data: {
                                                    id: id,
                                                    inputData: JSON.stringify(formData),
                                                }
                                            }).then((r) => {
                                                if (r?.data?.errorCode === 0) {
                                                    setIsEditInput(false);
                                                    setEditStates(prev => ({...prev, '2': false})); // 添加这行
                                                    message.success('修改成功');
                                                    doPostSearch({
                                                        data: {
                                                            aiPluginToolId: id
                                                        }
                                                    });
                                                }
                                            })
                                        });
                                    }
                                    // 保存输出参数
                                    else if (index === '3') {
                                        formOutput.validateFields().then(() => {
                                            const formData = formOutput.getFieldsValue();
                                    if (index === '1') {
                                        formBasicInfo.validateFields().then((values) => {
                                            doPostUpdate({
                                                data: {
                                                    id: id,
                                                    outputData: JSON.stringify(formData),
                                                    id: values.id,
                                                    name: values.name,
                                                    description: values.description,
                                                    basePath: values.basePath,
                                                    requestMethod: values.requestMethod
                                                }
                                            }).then((r) => {
                                                if (r?.data?.errorCode === 0) {
                                                    setIsEditOutput(false);
                                                    setEditStates(prev => ({...prev, '3': false})); // 添加这行
                                                    message.success('修改成功');
                                                    setEditStates(prev => ({...prev, '1': false})); // 添加这行
                                                    doPostSearch({
                                                        data: {
                                                            aiPluginToolId: id
                                                        }
                                                    });
                                                }
                                            })
                                        });
                                            });
                                    });
                                    }
                                });
                                    else if (index === '2' || index === '3') {
                                        //@ts-ignore
                                        if (pluginRef.current && pluginRef.current.handleSubmitParams) {
                                            //@ts-ignore
                                            pluginRef.current.handleSubmitParams(); // 主动触发子组件提交
                                        }
                                    }
                            }}
                        >
                            保存
@@ -581,7 +199,54 @@
            </div>
        );
    };
    const handleSubmit = (submittedParams: TreeTableNode[]) => {
        console.log('isEditInput')
        console.log(isEditInput)
        console.log('isEditOutput')
        console.log(isEditOutput)
        console.log('父组件收到的参数:', submittedParams);
        if (isEditInput){
            setIsEditInput(false)
                doPostUpdate({
                    data: {
                        id: id,
                        inputData: JSON.stringify(submittedParams),
                    }
                }).then((r) => {
                    if (r?.data?.errorCode === 0) {
                        setIsEditInput(false);
                        setEditStates(prev => ({...prev, '2': false})); // 添加这行
                        message.success('修改成功');
                        doPostSearch({
                            data: {
                                aiPluginToolId: id
                            }
                        });
                    }
                })
        } else {
            setIsEditOutput(false)
                    doPostUpdate({
                        data: {
                            id: id,
                            outputData: JSON.stringify(submittedParams),
                        }
                    }).then((r) => {
                        if (r?.data?.errorCode === 0) {
                            setIsEditOutput(false);
                            setEditStates(prev => ({...prev, '3': false})); // 添加这行
                            message.success('修改成功');
                            doPostSearch({
                                data: {
                                    aiPluginToolId: id
                                }
                            });
                        }
                    })
        }
        // 这里可以执行提交到API等操作
    };
    const collapseItems = [
        {
            key: '1',
@@ -640,32 +305,12 @@
            key: '2',
            label: '配置输入参数',
            children:  (
                <div>
                    <Form form={formInput} component={false} >
                        <Table
                            className="custom-table"
                            bordered
                            dataSource={inputData || []}
                            columns={getColumnsInput()}
                            pagination={false}
                            rowKey="key"
                        <PluginInputAndOutputData
                        value={inputDataTree}
                        editable={isEditInput}
                        ref={pluginRef}
                        onSubmit={handleSubmit}
                        />
                    </Form>
                    <Space style={{ marginTop: 16 }}>
                        {
                            isEditInput ? ( <Button
                                    type="dashed"
                                    icon={<PlusOutlined />}
                                    onClick={handleAdd}
                                >
                                    新增参数
                                </Button>
                            ) : (<></>
                            )
                        }
                    </Space>
                </div>
            ),
            extra: editPluginTool('2')
        },
@@ -673,32 +318,13 @@
            key: '3',
            label: '配置输出参数',
            children: (
                <div>
                    <Form form={formOutput} component={false} >
                        <Table
                            className="custom-table"
                            bordered
                            dataSource={outputData || []}
                            columns={getColumnsOutput()}
                            pagination={false}
                            rowKey="key"
                        />
                    </Form>
                    <Space style={{ marginTop: 16 }}>
                        {
                            isEditOutput ? ( <Button
                                    type="dashed"
                                    icon={<PlusOutlined />}
                                    onClick={handleAddOutputData}
                                >
                                    新增参数
                                </Button>
                            ) : (<></>
                            )
                        }
                    </Space>
                </div>
                <PluginInputAndOutputData
                    value={outputDataTree}
                    editable={isEditOutput}
                    ref={pluginRef}
                    onSubmit={handleSubmit}
                    isEditOutput={true}
                />
            ),
            extra: editPluginTool('3')
        },
@@ -732,4 +358,4 @@
export default {
    path: "/ai/pluginToolEdit",
    element: PluginToolEdit
};
};