From 2f79a2a04d7bdf1ccb9ff54cce6c39aa21f7b7cb Mon Sep 17 00:00:00 2001 From: balibabu Date: Wed, 9 Jul 2025 12:17:01 +0800 Subject: [PATCH] Feat: Display MCP multiple selection bar #3221 (#8737) ### What problem does this PR solve? Feat: Display MCP multiple selection bar #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/bulk-operate-bar.tsx | 14 +++-- web/src/hooks/use-mcp-request.ts | 4 +- web/src/pages/agent/chat/hooks.ts | 6 +-- web/src/pages/agent/constant.tsx | 2 +- .../agent/form/message-form/use-values.ts | 7 +-- .../profile-setting/mcp/edit-mcp-dialog.tsx | 2 +- .../profile-setting/mcp/edit-mcp-form.tsx | 8 +-- web/src/pages/profile-setting/mcp/index.tsx | 22 ++++++-- .../pages/profile-setting/mcp/mcp-card.tsx | 51 +++++++++++-------- .../mcp/use-bulk-operate-mcp.tsx | 37 ++++++++++++++ .../pages/profile-setting/mcp/use-edit-mcp.ts | 10 ++-- web/src/utils/request.ts | 4 +- 12 files changed, 115 insertions(+), 52 deletions(-) create mode 100644 web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx diff --git a/web/src/components/bulk-operate-bar.tsx b/web/src/components/bulk-operate-bar.tsx index 51652ff72..c15e66c75 100644 --- a/web/src/components/bulk-operate-bar.tsx +++ b/web/src/components/bulk-operate-bar.tsx @@ -13,15 +13,23 @@ export type BulkOperateItemType = { onClick(): void; }; -type BulkOperateBarProps = { list: BulkOperateItemType[]; count: number }; +type BulkOperateBarProps = { + list: BulkOperateItemType[]; + count: number; + className?: string; +}; -export function BulkOperateBar({ list, count }: BulkOperateBarProps) { +export function BulkOperateBar({ + list, + count, + className, +}: BulkOperateBarProps) { const isDeleteItem = useCallback((id: string) => { return id === 'delete'; }, []); return ( - +
Selected: {count} Files diff --git a/web/src/hooks/use-mcp-request.ts b/web/src/hooks/use-mcp-request.ts index aaf5fe4e1..522c35404 100644 --- a/web/src/hooks/use-mcp-request.ts +++ b/web/src/hooks/use-mcp-request.ts @@ -66,7 +66,7 @@ export const useCreateMcpServer = () => { queryKey: [McpApiAction.ListMcpServer], }); } - return data; + return data.code; }, }); @@ -90,7 +90,7 @@ export const useUpdateMcpServer = () => { queryKey: [McpApiAction.ListMcpServer], }); } - return data; + return data.code; }, }); diff --git a/web/src/pages/agent/chat/hooks.ts b/web/src/pages/agent/chat/hooks.ts index 3813e8e8f..0f3a8203b 100644 --- a/web/src/pages/agent/chat/hooks.ts +++ b/web/src/pages/agent/chat/hooks.ts @@ -1,3 +1,4 @@ +import sonnerMessage from '@/components/ui/message'; import { MessageType } from '@/constants/chat'; import { useHandleMessageInputChange, @@ -14,7 +15,6 @@ import { import { Message } from '@/interfaces/database/chat'; import i18n from '@/locales/config'; import api from '@/utils/api'; -import { message } from 'antd'; import { get } from 'lodash'; import trim from 'lodash/trim'; import { useCallback, useContext, useEffect, useMemo } from 'react'; @@ -28,8 +28,6 @@ import { BeginQuery } from '../interface'; import useGraphStore from '../store'; import { receiveMessageError } from '../utils'; -const antMessage = message; - export const useSelectNextMessages = () => { const { data: flowDetail, loading } = useFetchAgent(); const reference = flowDetail.dsl.retrieval; @@ -139,7 +137,7 @@ export const useSendNextMessage = () => { const res = await send(params); if (receiveMessageError(res)) { - antMessage.error(res?.data?.message); + sonnerMessage.error(res?.data?.message); // cancel loading setValue(message.content); diff --git a/web/src/pages/agent/constant.tsx b/web/src/pages/agent/constant.tsx index 788a697e5..74c2fb86d 100644 --- a/web/src/pages/agent/constant.tsx +++ b/web/src/pages/agent/constant.tsx @@ -465,7 +465,7 @@ export const initialCategorizeValues = { }; export const initialMessageValues = { - messages: [], + content: [''], }; export const initialKeywordExtractValues = { diff --git a/web/src/pages/agent/form/message-form/use-values.ts b/web/src/pages/agent/form/message-form/use-values.ts index 415314665..6a90881be 100644 --- a/web/src/pages/agent/form/message-form/use-values.ts +++ b/web/src/pages/agent/form/message-form/use-values.ts @@ -1,18 +1,15 @@ import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { isEmpty } from 'lodash'; import { useMemo } from 'react'; +import { initialMessageValues } from '../../constant'; import { convertToObjectArray } from '../../utils'; -const defaultValues = { - content: [], -}; - export function useValues(node?: RAGFlowNodeType) { const values = useMemo(() => { const formData = node?.data?.form; if (isEmpty(formData)) { - return defaultValues; + return initialMessageValues; } return { diff --git a/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx b/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx index f149d502f..ad98c6ff3 100644 --- a/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx +++ b/web/src/pages/profile-setting/mcp/edit-mcp-dialog.tsx @@ -19,7 +19,7 @@ export function EditMcpDialog({ hideModal, loading, onOk }: IModalProps) { Edit profile - + {t('common.save')} diff --git a/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx b/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx index a017dbf85..8daa6f494 100644 --- a/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx +++ b/web/src/pages/profile-setting/mcp/edit-mcp-form.tsx @@ -30,7 +30,6 @@ const ServerTypeOptions = buildOptions(ServerType); export function EditMcpForm({ initialName, - hideModal, onOk, }: IModalProps & { initialName?: string }) { const { t } = useTranslation(); @@ -61,11 +60,8 @@ export function EditMcpForm({ defaultValues: { name: '', server_type: ServerType.SSE, url: '' }, }); - async function onSubmit(data: z.infer) { - const ret = await onOk?.(data); - if (ret) { - hideModal?.(); - } + function onSubmit(data: z.infer) { + onOk?.(data); } useEffect(() => { diff --git a/web/src/pages/profile-setting/mcp/index.tsx b/web/src/pages/profile-setting/mcp/index.tsx index c596cca4f..1df95269d 100644 --- a/web/src/pages/profile-setting/mcp/index.tsx +++ b/web/src/pages/profile-setting/mcp/index.tsx @@ -1,20 +1,22 @@ +import { BulkOperateBar } from '@/components/bulk-operate-bar'; import { Button } from '@/components/ui/button'; import { SearchInput } from '@/components/ui/input'; import { useListMcpServer } from '@/hooks/use-mcp-request'; import { Import, Plus } from 'lucide-react'; import { EditMcpDialog } from './edit-mcp-dialog'; import { McpCard } from './mcp-card'; +import { useBulkOperateMCP } from './use-bulk-operate-mcp'; import { useEditMcp } from './use-edit-mcp'; -const list = new Array(10).fill('1'); export default function McpServer() { const { data } = useListMcpServer(); const { editVisible, showEditModal, hideEditModal, handleOk } = useEditMcp(); + const { list, selectedList, handleSelectChange } = useBulkOperateMCP(); return (
MCP Servers
-
+
自定义 MCP Server 的列表
@@ -26,9 +28,21 @@ export default function McpServer() {
-
+ {selectedList.length > 0 && ( + + )} +
{data.mcp_servers.map((item) => ( - + ))}
{editVisible && ( diff --git a/web/src/pages/profile-setting/mcp/mcp-card.tsx b/web/src/pages/profile-setting/mcp/mcp-card.tsx index e255e3238..07eded047 100644 --- a/web/src/pages/profile-setting/mcp/mcp-card.tsx +++ b/web/src/pages/profile-setting/mcp/mcp-card.tsx @@ -1,39 +1,48 @@ import { MoreButton } from '@/components/more-button'; -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Card, CardContent } from '@/components/ui/card'; -import { useNavigatePage } from '@/hooks/logic-hooks/navigate-hooks'; +import { Checkbox } from '@/components/ui/checkbox'; import { IMcpServer } from '@/interfaces/database/mcp'; import { formatDate } from '@/utils/date'; import { McpDropdown } from './mcp-dropdown'; +import { UseBulkOperateMCPReturnType } from './use-bulk-operate-mcp'; export type DatasetCardProps = { data: IMcpServer; -}; - -export function McpCard({ data }: DatasetCardProps) { - const { navigateToAgent } = useNavigatePage(); +} & Pick; +export function McpCard({ + data, + selectedList, + handleSelectChange, +}: DatasetCardProps) { return ( - + -
-
- - - CN - +
+

{data.name}

+
+ + + + { + if (typeof checked === 'boolean') { + handleSelectChange(data.id, checked); + } + }} + onClick={(e) => { + e.stopPropagation(); + }} + />
- - -
-

- {data.name} -

-

{data.description}

-

+

+ 20 cached tools +
+

{formatDate(data.update_date)}

diff --git a/web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx b/web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx new file mode 100644 index 000000000..79820079d --- /dev/null +++ b/web/src/pages/profile-setting/mcp/use-bulk-operate-mcp.tsx @@ -0,0 +1,37 @@ +import { Trash2, Upload } from 'lucide-react'; +import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +export function useBulkOperateMCP() { + const { t } = useTranslation(); + const [selectedList, setSelectedList] = useState>([]); + + const handleEnableClick = useCallback(() => {}, []); + + const handleDelete = useCallback(() => {}, []); + + const handleSelectChange = useCallback((id: string, checked: boolean) => { + setSelectedList((list) => { + return checked ? [...list, id] : list.filter((item) => item !== id); + }); + }, []); + + const list = [ + { + id: 'export', + label: t('mcp.export'), + icon: , + onClick: handleEnableClick, + }, + { + id: 'delete', + label: t('common.delete'), + icon: , + onClick: handleDelete, + }, + ]; + + return { list, selectedList, handleSelectChange }; +} + +export type UseBulkOperateMCPReturnType = ReturnType; diff --git a/web/src/pages/profile-setting/mcp/use-edit-mcp.ts b/web/src/pages/profile-setting/mcp/use-edit-mcp.ts index 0e21b0a4e..903929555 100644 --- a/web/src/pages/profile-setting/mcp/use-edit-mcp.ts +++ b/web/src/pages/profile-setting/mcp/use-edit-mcp.ts @@ -28,13 +28,17 @@ export const useEditMcp = () => { const handleOk = useCallback( async (values: any) => { + let code; if (id) { - updateMcpServer(values); + code = await updateMcpServer(values); } else { - createMcpServer(values); + code = await createMcpServer(values); + } + if (code === 0) { + hideEditModal(); } }, - [createMcpServer, id, updateMcpServer], + [createMcpServer, hideEditModal, id, updateMcpServer], ); return { diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index 4b9d936cd..91d8dc26c 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -11,7 +11,7 @@ import { convertTheKeysOfTheObjectToSnake } from './common-util'; const FAILED_TO_FETCH = 'Failed to fetch'; -const RetcodeMessage = { +export const RetcodeMessage = { 200: i18n.t('message.200'), 201: i18n.t('message.201'), 202: i18n.t('message.202'), @@ -29,7 +29,7 @@ const RetcodeMessage = { 503: i18n.t('message.503'), 504: i18n.t('message.504'), }; -type ResultCode = +export type ResultCode = | 200 | 201 | 202