44 * Licensed under the MIT License. See License.txt in the project root for license information.
55 *--------------------------------------------------------------------------------------------*/
66
7- import React , { useState } from 'react' ;
7+ import React , { useState , useRef } from 'react' ;
88import { Collapse , Button , Divider , Tag } from 'antd' ;
99import { getPluginDetail , getChatbotPluginDetail } from '@/shared/http/plugin' ;
1010import { useTranslation } from 'react-i18next' ;
@@ -43,8 +43,11 @@ const ToolTable = (props: any) => {
4343 searchName,
4444 } = props ;
4545 const [ getPluginData , setGetPluginData ] = useState < any > ( [ ] ) ;
46+ const [ expandedKeys , setExpandedKeys ] = useState < string [ ] > ( [ ] ) ;
4647 const modalTypes = [ 'pluginButtonTool' , 'loop' ] ;
4748 let checkedToolList : any = [ ] ;
49+ // 用于标记是否正在进行批量添加操作
50+ const isBatchAddingRef = useRef ( false ) ;
4851
4952 const confirm = ( item : any ) => {
5053 const pluginInfo = pluginData ?. mapType ? [ pluginData ] : getPluginData ;
@@ -83,8 +86,94 @@ const ToolTable = (props: any) => {
8386 }
8487 } ;
8588
89+ // 批量添加处理函数
90+ const handleBatchAdd = ( ) => {
91+ // 标记正在进行批量添加操作
92+ isBatchAddingRef . current = true ;
93+
94+ const unselectedTools = getPluginData . filter ( ( item : any ) => ! item . isChecked ) ;
95+
96+ if ( unselectedTools . length === 0 ) {
97+ isBatchAddingRef . current = false ;
98+ return ;
99+ }
100+
101+ // 获取当前插件的 pluginId,用于过滤工具
102+ const currentPluginId = pluginData ?. pluginId ;
103+
104+ // 在 addSkill 模式下,我们需要确保只处理当前插件的工具
105+ // 先过滤掉字符串类型的项,然后只添加当前插件的工具
106+ if ( type === 'addSkill' ) {
107+ // 过滤掉字符串类型的项(这些是之前添加的其他插件的工具)
108+ const objectTools = checkedList . current . filter ( ( item : any ) => {
109+ return typeof item === 'object' && item !== null && ! Array . isArray ( item ) ;
110+ } ) ;
111+
112+ // 进一步过滤,只保留当前插件的工具
113+ // 工具对象的 pluginId 可能直接存在,也可能需要从父插件对象中获取
114+ const currentPluginTools = objectTools . filter ( ( item : any ) => {
115+ const itemPluginId = item ?. pluginId || item ?. pluginData ?. pluginId ;
116+ return itemPluginId === currentPluginId ;
117+ } ) ;
118+
119+ // 重置 checkedList.current,只保留当前插件的工具
120+ checkedList . current = [ ...currentPluginTools ] ;
121+ }
122+
123+ // 添加当前未选中的工具
124+ unselectedTools . forEach ( ( item : any ) => {
125+ // 确保工具属于当前插件
126+ // 如果工具对象没有 pluginId,则从父插件对象中获取
127+ const itemPluginId = item ?. pluginId || pluginData ?. pluginId ;
128+
129+ if ( itemPluginId === currentPluginId ) {
130+ // 确保工具对象有 pluginId 属性
131+ if ( ! item . pluginId ) {
132+ item . pluginId = currentPluginId ;
133+ }
134+ item . isChecked = true ;
135+ checkedList . current . push ( item ) ;
136+ }
137+ } ) ;
138+
139+ // 保存当前的 panelKey,确保面板保持展开
140+ const currentPanelKey = pluginData . appCategory ? pluginData . uniqueName : pluginData . pluginId ;
141+
142+ // 更新 UI 状态
143+ let newData = deepClone ( getPluginData ) ;
144+ newData . forEach ( ( ite : any ) => {
145+ if ( ! ite . isChecked ) {
146+ ite . isChecked = true ;
147+ }
148+ } ) ;
149+ setGetPluginData ( newData ) ;
150+
151+ // 确保面板保持展开状态
152+ // 如果当前 panelKey 不在 expandedKeys 中,则添加它
153+ if ( ! expandedKeys . includes ( currentPanelKey ) ) {
154+ setExpandedKeys ( [ ...expandedKeys , currentPanelKey ] ) ;
155+ }
156+
157+ // 调用添加方法
158+ if ( type === 'addSkill' ) {
159+ workflowAdd ( ) ;
160+ }
161+
162+ // 延迟重置批量添加标志,确保父组件重新渲染完成后再重置
163+ // 这样可以避免父组件重新渲染时触发的 onChange 关闭面板
164+ setTimeout ( ( ) => {
165+ isBatchAddingRef . current = false ;
166+ } , 500 ) ;
167+ } ;
168+
86169 // 通用HTML
87170 const fncHTML = ( item : any , toolType : string ) => {
171+ const panelKey = pluginData . appCategory ? pluginData . uniqueName : pluginData . pluginId ;
172+ const isMcpPlugin = pluginData . extension ?. type === 'mcp' ;
173+ const isExpanded = expandedKeys . includes ( panelKey ) ;
174+ const hasUnselectedTools = getPluginData . some ( ( tool : any ) => ! tool . isChecked ) ;
175+ const showBatchAdd = isMcpPlugin && isExpanded && toolType === 'panel' && type === 'addSkill' && getPluginData . length > 0 && hasUnselectedTools ;
176+
88177 return (
89178 < div className = 'tool-table' >
90179 < div className = 'tool-table-header' >
@@ -127,8 +216,17 @@ const ToolTable = (props: any) => {
127216 { toolType === 'panel' ? item . extension . description : item . description }
128217 </ div >
129218 </ div >
130- { ( toolType === ToolType . TOOL || toolType === ToolType . WATERFLOW ) && (
131- < div >
219+ < div className = 'tool-table-actions' >
220+ { showBatchAdd && (
221+ < Button
222+ type = 'primary'
223+ onClick = { handleBatchAdd }
224+ className = 'batch-add-button'
225+ >
226+ { t ( '批量添加' ) || 'batchAdd' }
227+ </ Button >
228+ ) }
229+ { ( toolType === ToolType . TOOL || toolType === ToolType . WATERFLOW ) && (
132230 < Button
133231 disabled = { type === 'addSkill' ? item . isChecked : false }
134232 onClick = { ( ) => confirm ( item . uniqueName ) }
@@ -148,16 +246,34 @@ const ToolTable = (props: any) => {
148246 t ( 'additions' )
149247 ) }
150248 </ Button >
151- </ div >
152- ) }
249+ ) }
250+ </ div >
153251 </ div >
154252 </ div >
155253 </ div >
156254 ) ;
157255 } ;
158256
159257 const onChange = async ( e : any ) => {
160- let param = e . toString ( ) ;
258+ const panelKey = pluginData . appCategory ? pluginData . uniqueName : pluginData . pluginId ;
259+
260+ // 更新展开状态 - Collapse 的 onChange 接收的是展开 key 的数组
261+ const expandedArray = Array . isArray ( e ) ? e : ( e ? [ e ] : [ ] ) ;
262+ const isCurrentlyExpanded = expandedArray . includes ( panelKey ) ;
263+ const isTryingToClose = ! isCurrentlyExpanded && expandedKeys . includes ( panelKey ) ;
264+
265+ // 如果正在进行批量添加操作,且尝试关闭当前面板,则阻止关闭
266+ if ( isBatchAddingRef . current && isTryingToClose ) {
267+ // 保持面板展开状态
268+ setExpandedKeys ( [ ...expandedKeys . filter ( key => key !== panelKey ) , panelKey ] ) ;
269+ return ;
270+ }
271+
272+ // 更新展开状态
273+ setExpandedKeys ( expandedArray ) ;
274+
275+ let param = expandedArray . length > 0 ? panelKey : '' ;
276+
161277 if ( type === 'addSkill' ) {
162278 checkData . forEach ( ( item : any ) => {
163279 checkedToolList . push ( item . name ) ;
@@ -181,15 +297,23 @@ const ToolTable = (props: any) => {
181297 setGetPluginData ( newRes ) ;
182298 }
183299 }
184- } catch { }
300+ } catch ( error ) {
301+ // 静默处理错误
302+ }
185303 } ;
186304
305+ const panelKey = pluginData . appCategory ? pluginData . uniqueName : pluginData . pluginId ;
306+
187307 return (
188308 < >
189- < Collapse expandIconPosition = 'end' onChange = { onChange } >
309+ < Collapse
310+ expandIconPosition = 'end'
311+ onChange = { onChange }
312+ activeKey = { expandedKeys }
313+ >
190314 < Collapse . Panel
191315 header = { fncHTML ( pluginData , 'panel' ) }
192- key = { pluginData . appCategory ? pluginData . uniqueName : pluginData . pluginId }
316+ key = { panelKey }
193317 >
194318 { getPluginData . map ( ( item : any , index : any ) => {
195319 return (
0 commit comments