feat(web): skill toolList add is_active

This commit is contained in:
zhaoying
2026-04-01 13:33:16 +08:00
parent d3cd66fc6e
commit e77a1a92fd
4 changed files with 130 additions and 116 deletions

View File

@@ -19,6 +19,7 @@
import { Outlet } from 'react-router-dom'; import { Outlet } from 'react-router-dom';
import { useEffect, type FC } from 'react'; import { useEffect, type FC } from 'react';
import { Layout } from 'antd';
import { useUser } from '@/store/user'; import { useUser } from '@/store/user';
@@ -35,10 +36,10 @@ const BasicAuthLayout: FC = () => {
}, [getUserInfo]); }, [getUserInfo]);
return ( return (
<div className="rb:relative rb:min-h-screen rb:w-screen"> <Layout className="rb:min-h-screen!">
{/* Render child routes without additional UI */} {/* Render child routes without additional UI */}
<Outlet /> <Outlet />
</div> </Layout>
) )
}; };

View File

@@ -12,7 +12,7 @@
import { type FC, useRef, useState, useEffect } from 'react' import { type FC, useRef, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { Space, Button, List } from 'antd' import { Space, Button, Flex } from 'antd'
import Card from '@/views/ApplicationConfig/components/Card' import Card from '@/views/ApplicationConfig/components/Card'
import type { import type {
@@ -22,6 +22,7 @@ import type {
import Empty from '@/components/Empty' import Empty from '@/components/Empty'
import ToolModal from './ToolModal' import ToolModal from './ToolModal'
import { getToolMethods, getToolDetail } from '@/api/tools' import { getToolMethods, getToolDetail } from '@/api/tools'
import Tag from '@/components/Tag'
/** /**
* Tool List Component Props * Tool List Component Props
@@ -61,6 +62,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
const mcpFilterItem = (methods as any[]).find(vo => vo.name === item.operation) const mcpFilterItem = (methods as any[]).find(vo => vo.name === item.operation)
return { return {
...item, ...item,
is_active: (toolDetail as any).is_active,
label: mcpFilterItem?.description, label: mcpFilterItem?.description,
method_id: mcpFilterItem?.method_id, method_id: mcpFilterItem?.method_id,
value: mcpFilterItem?.name, value: mcpFilterItem?.name,
@@ -74,6 +76,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
const builtinFilterItem = (methods as any[]).find(vo => vo.name === item.operation) const builtinFilterItem = (methods as any[]).find(vo => vo.name === item.operation)
return { return {
...item, ...item,
is_active: (toolDetail as any).is_active,
label: builtinFilterItem?.description, label: builtinFilterItem?.description,
method_id: builtinFilterItem?.method_id, method_id: builtinFilterItem?.method_id,
value: builtinFilterItem?.name, value: builtinFilterItem?.name,
@@ -84,6 +87,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
// Single method: Use first method // Single method: Use first method
return { return {
...item, ...item,
is_active: (toolDetail as any).is_active,
label: (methods as any[])[0]?.description, label: (methods as any[])[0]?.description,
method_id: (methods as any[])[0]?.method_id, method_id: (methods as any[])[0]?.method_id,
value: (methods as any[])[0]?.name, value: (methods as any[])[0]?.name,
@@ -96,6 +100,7 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
const customFilterItem = (methods as any[]).find(vo => vo.method_id === item.operation) const customFilterItem = (methods as any[]).find(vo => vo.method_id === item.operation)
return { return {
...item, ...item,
is_active: (toolDetail as any).is_active,
label: customFilterItem?.name, label: customFilterItem?.name,
method_id: customFilterItem?.method_id, method_id: customFilterItem?.method_id,
value: customFilterItem?.name, value: customFilterItem?.name,
@@ -129,7 +134,10 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
* @param tool - Tool to add * @param tool - Tool to add
*/ */
const updateTools = (tool: ToolOption) => { const updateTools = (tool: ToolOption) => {
const list = [...toolList, tool] const list = [...toolList, {
...tool,
is_active: true,
}]
setToolList(list) setToolList(list)
onChange && onChange(list) onChange && onChange(list)
} }
@@ -149,39 +157,32 @@ const ToolList: FC<ToolListProps> = ({value, onChange}) => {
<Card <Card
title={t('application.toolConfiguration')} title={t('application.toolConfiguration')}
extra={ extra={
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handleAddTool}> <Button className="rb:h-6! rb:py-0! rb:px-2! rb:rounded-md! rb:text-[#21233" onClick={handleAddTool}>+ {t('application.addTool')}</Button>
+ {t('application.addTool')}
</Button>
} }
> >
{/* Show empty state or tool list */}
{toolList.length === 0 {toolList.length === 0
? <Empty size={88} /> ? <div className="rb-border rb:rounded-xl rb:pt-4 rb:pb-6"><Empty size={88} /></div>
: : <Flex vertical gap={12}>
<List {toolList.map((item, index) => (
grid={{ gutter: 12, column: 1 }} <Flex key={index} align="center" justify="space-between" className="rb:py-2.5! rb:pl-4! rb:pr-3! rb-border rb:rounded-lg">
dataSource={toolList} <div>
renderItem={(item, index) => (
<List.Item>
{/* Tool card with delete button */}
<div key={index} className="rb:flex rb:items-center rb:justify-between rb:p-[12px_16px] rb:bg-[#FBFDFF] rb:border rb:border-[#DFE4ED] rb:rounded-lg">
{/* Tool label/description */}
<div className="rb:font-medium rb:leading-4"> <div className="rb:font-medium rb:leading-4">
{item.label} {item.label}
</div> </div>
<Tag color={item.is_active ? 'success' : 'error'} className="rb:mt-1">
{item.is_active ? t('common.enable') : t('common.deleted')}
</Tag>
</div>
<Space size={12}> <Space size={12}>
{/* Delete button with hover effect */}
<div <div
className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]" className="rb:w-6 rb:h-6 rb:cursor-pointer rb:bg-[url('@/assets/images/deleteBorder.svg')] rb:hover:bg-[url('@/assets/images/deleteBg.svg')]"
onClick={() => handleDeleteTool(index)} onClick={() => handleDeleteTool(index)}
></div> ></div>
</Space> </Space>
</div> </Flex>
</List.Item> ))}
)} </Flex>
/>
} }
{/* Tool selection modal */}
<ToolModal <ToolModal
ref={toolModalRef} ref={toolModalRef}
refresh={updateTools} refresh={updateTools}

View File

@@ -32,6 +32,7 @@ export interface ToolOption {
tool_id?: string; tool_id?: string;
/** Whether tool is enabled */ /** Whether tool is enabled */
enabled?: boolean; enabled?: boolean;
is_active?: boolean;
} }
/** /**

View File

@@ -7,17 +7,16 @@
import { type FC, useEffect, useRef, useState } from "react"; import { type FC, useEffect, useRef, useState } from "react";
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
import { Form, Input, Button, Space, Select, App } from 'antd' import { Form, Input, Button, Space, Select, App, Flex } from 'antd'
import Card from '@/views/ApplicationConfig/components/Card' import Card from '@/views/ApplicationConfig/components/Card'
import aiPrompt from '@/assets/images/application/aiPrompt.png'
import AiPromptModal from '@/views/ApplicationConfig/components/AiPromptModal' import AiPromptModal from '@/views/ApplicationConfig/components/AiPromptModal'
import ToolList from '../components/ToolList/ToolList' import ToolList from '../components/ToolList/ToolList'
import type { AiPromptModalRef } from '@/views/ApplicationConfig/types' import type { AiPromptModalRef } from '@/views/ApplicationConfig/types'
import exitIcon from '@/assets/images/knowledgeBase/exit.png';
import type { SkillFormData } from '../types' import type { SkillFormData } from '../types'
import { getSkillDetail, createSkill, updateSkill } from '@/api/skill' import { getSkillDetail, createSkill, updateSkill } from '@/api/skill'
import { stringRegExp } from '@/utils/validator'; import { stringRegExp } from '@/utils/validator';
import PageHeader from '@/components/Layout/PageHeader'
/** /**
* Skill Configuration Page Component * Skill Configuration Page Component
@@ -43,6 +42,7 @@ const SkillConfig: FC = () => {
const { message } = App.useApp() const { message } = App.useApp()
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [form] = Form.useForm<SkillFormData>(); const [form] = Form.useForm<SkillFormData>();
const [data, setData] = useState<SkillFormData | null>(null)
/** /**
* Effect: Load skill data if editing existing skill * Effect: Load skill data if editing existing skill
@@ -70,6 +70,7 @@ const SkillConfig: FC = () => {
getSkillDetail(id) getSkillDetail(id)
.then(res => { .then(res => {
form.setFieldsValue(res as SkillFormData) form.setFieldsValue(res as SkillFormData)
setData(res as SkillFormData)
}) })
.finally(() => { .finally(() => {
setLoading(false) setLoading(false)
@@ -131,13 +132,24 @@ const SkillConfig: FC = () => {
} }
return ( return (
<div className="rb:w-250 rb:mt-5 rb:pb-5 rb:mx-auto"> <Flex vertical className="rb:h-screen!">
{/* Back button */} <PageHeader
<div className='rb:flex rb:items-center rb:gap-2 rb:mb-4 rb:cursor-pointer' onClick={handleBack}> title={data?.name}
<img src={exitIcon} alt='exit' className='rb:w-4 rb:h-4' /> extra={
<span className='rb:text-gray-500 rb:text-sm'>{t('common.exit')}</span> <Flex gap={12} align="center">
</div> {/* Save button */}
<Button type="primary" className="rb:px-2! rb:gap-0.5!" disabled={loading} onClick={handleSave}>{t('skills.save')}</Button>
<Button
className="rb:px-2! rb:gap-0.5!"
icon={<div className="rb:bg-[url('@/assets/images/workflow/return.svg')] rb:size-4 rb:bg-cover"></div>}
onClick={handleBack}
>
{t('common.return')}
</Button>
</Flex>
}
/>
<div className="rb:w-250 rb:my-3 rb:mx-auto rb:flex-1 rb:overflow-y-auto">
<Form form={form} layout="vertical"> <Form form={form} layout="vertical">
<Space size={16} direction="vertical" className="rb:w-full"> <Space size={16} direction="vertical" className="rb:w-full">
{/* Manifest Section: Basic skill information */} {/* Manifest Section: Basic skill information */}
@@ -176,7 +188,7 @@ const SkillConfig: FC = () => {
<Card title={t('skills.promptConfiguration')} <Card title={t('skills.promptConfiguration')}
extra={ extra={
<Button style={{ padding: '0 8px', height: '24px' }} onClick={handlePrompt}> <Button style={{ padding: '0 8px', height: '24px' }} onClick={handlePrompt}>
<img src={aiPrompt} className="rb:size-5" /> <div className="rb:size-5 rb:bg-cover rb:bg-[url('@/assets/images/application/aiPrompt.png')] rb:mr-1!" />
{t('skills.aiPrompt')} {t('skills.aiPrompt')}
</Button> </Button>
} }
@@ -206,8 +218,6 @@ const SkillConfig: FC = () => {
<ToolList /> <ToolList />
</Form.Item> </Form.Item>
{/* Save button */}
<Button type="primary" block disabled={loading} onClick={handleSave}>{t('skills.save')}</Button>
</Space> </Space>
</Form> </Form>
@@ -218,6 +228,7 @@ const SkillConfig: FC = () => {
source="skills" source="skills"
/> />
</div> </div>
</Flex>
) )
} }