Feat: Adjust the style of the canvas node #10703 (#10795)

### What problem does this PR solve?

Feat: Adjust the style of the canvas node #10703


### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-10-27 10:36:36 +08:00
committed by GitHub
parent 50e93d1528
commit 24ab857471
122 changed files with 290 additions and 7156 deletions

View File

@ -1,13 +1,22 @@
import { BulkOperateBar } from '@/components/bulk-operate-bar';
import { CardContainer } from '@/components/card-container';
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import Spotlight from '@/components/spotlight';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
import { SearchInput } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { RAGFlowPagination } from '@/components/ui/ragflow-pagination';
import { useListMcpServer } from '@/hooks/use-mcp-request';
import { pick } from 'lodash';
import { Plus, SquareArrowOutDownLeft } from 'lucide-react';
import { useCallback } from 'react';
import {
Download,
LayoutList,
ListChecks,
Plus,
Trash2,
Upload,
} from 'lucide-react';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { ProfileSettingWrapperCard } from '../components';
import { EditMcpDialog } from './edit-mcp-dialog';
@ -22,11 +31,19 @@ export default function McpServer() {
useListMcpServer();
const { editVisible, showEditModal, hideEditModal, handleOk, id, loading } =
useEditMcp();
const { list, selectedList, handleSelectChange } = useBulkOperateMCP();
const {
selectedList,
handleSelectChange,
handleDelete,
handleExportMcp,
handleSelectAll,
} = useBulkOperateMCP(data.mcp_servers);
const { t } = useTranslation();
const { importVisible, showImportModal, hideImportModal, onImportOk } =
useImportMcp();
const [isSelectionMode, setSelectionMode] = useState(false);
const handlePageChange = useCallback(
(page: number, pageSize?: number) => {
setPagination({ page, pageSize });
@ -34,6 +51,10 @@ export default function McpServer() {
[setPagination],
);
const switchSelectionMode = useCallback(() => {
setSelectionMode((prev) => !prev);
}, []);
return (
<ProfileSettingWrapperCard
header={
@ -51,23 +72,51 @@ export default function McpServer() {
value={searchString}
onChange={handleInputChange}
></SearchInput>
<Button onClick={showEditModal('')}>
<Plus /> {t('mcp.addMCP')}
<Button variant={'secondary'} onClick={switchSelectionMode}>
{isSelectionMode ? (
<ListChecks className="size-3.5" />
) : (
<LayoutList className="size-3.5" />
)}
{t(`mcp.${isSelectionMode ? 'exitBulkManage' : 'bulkManage'}`)}
</Button>
<Button variant={'secondary'} onClick={showImportModal}>
<SquareArrowOutDownLeft /> {t('mcp.import')}
<Button variant={'secondary'} onClick={showEditModal('')}>
<Plus className="size-3.5" /> {t('mcp.addMCP')}
</Button>
<Button onClick={showImportModal}>
<Download className="size-3.5" />
{t('mcp.import')}
</Button>
</div>
</section>
</>
}
>
{selectedList.length > 0 && (
<BulkOperateBar
list={list}
count={selectedList.length}
className="mb-2.5"
></BulkOperateBar>
{isSelectionMode && (
<section className="pb-5 flex items-center">
<Checkbox id="all" onCheckedChange={handleSelectAll} />
<Label
className="pl-2 text-text-primary cursor-pointer"
htmlFor="all"
>
{t('common.selectAll')}
</Label>
<span className="text-text-secondary pr-10 pl-5">
{t('mcp.selected')} {selectedList.length}
</span>
<div className="flex gap-10 items-center">
<Button variant={'secondary'} onClick={handleExportMcp}>
<Upload className="size-3.5"></Upload>
{t('mcp.export')}
</Button>
<ConfirmDeleteDialog onOk={handleDelete}>
<Button variant={'danger'}>
<Trash2 className="size-3.5 cursor-pointer" />
{t('common.delete')}
</Button>
</ConfirmDeleteDialog>
</div>
</section>
)}
<CardContainer>
{data.mcp_servers.map((item) => (
@ -77,6 +126,7 @@ export default function McpServer() {
selectedList={selectedList}
handleSelectChange={handleSelectChange}
showEditModal={showEditModal}
isSelectionMode={isSelectionMode}
></McpCard>
))}
</CardContainer>

View File

@ -1,4 +1,3 @@
import { MoreButton } from '@/components/more-button';
import { Card, CardContent } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { IMcpServer } from '@/interfaces/database/mcp';
@ -6,12 +5,13 @@ import { formatDate } from '@/utils/date';
import { isPlainObject } from 'lodash';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { McpDropdown } from './mcp-dropdown';
import { McpOperation } from './mcp-operation';
import { UseBulkOperateMCPReturnType } from './use-bulk-operate-mcp';
import { UseEditMcpReturnType } from './use-edit-mcp';
export type DatasetCardProps = {
data: IMcpServer;
isSelectionMode: boolean;
} & Pick<UseBulkOperateMCPReturnType, 'handleSelectChange' | 'selectedList'> &
Pick<UseEditMcpReturnType, 'showEditModal'>;
@ -20,6 +20,7 @@ export function McpCard({
selectedList,
handleSelectChange,
showEditModal,
isSelectionMode,
}: DatasetCardProps) {
const { t } = useTranslation();
const toolLength = useMemo(() => {
@ -34,6 +35,7 @@ export function McpCard({
handleSelectChange(data.id, checked);
}
};
return (
<Card key={data.id}>
<CardContent className="p-2.5 pt-2 group">
@ -42,16 +44,20 @@ export function McpCard({
{data.name}
</h3>
<div className="space-x-4">
<McpDropdown mcpId={data.id} showEditModal={showEditModal}>
<MoreButton></MoreButton>
</McpDropdown>
<Checkbox
checked={selectedList.includes(data.id)}
onCheckedChange={onCheckedChange}
onClick={(e) => {
e.stopPropagation();
}}
/>
{isSelectionMode ? (
<Checkbox
checked={selectedList.includes(data.id)}
onCheckedChange={onCheckedChange}
onClick={(e) => {
e.stopPropagation();
}}
/>
) : (
<McpOperation
mcpId={data.id}
showEditModal={showEditModal}
></McpOperation>
)}
</div>
</section>
<div className="flex justify-between items-end text-xs text-text-secondary">

View File

@ -1,60 +0,0 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { useDeleteMcpServer } from '@/hooks/use-mcp-request';
import { PenLine, Trash2, Upload } from 'lucide-react';
import { MouseEventHandler, PropsWithChildren, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { UseEditMcpReturnType } from './use-edit-mcp';
import { useExportMcp } from './use-export-mcp';
export function McpDropdown({
children,
mcpId,
showEditModal,
}: PropsWithChildren & { mcpId: string } & Pick<
UseEditMcpReturnType,
'showEditModal'
>) {
const { t } = useTranslation();
const { deleteMcpServer } = useDeleteMcpServer();
const { handleExportMcpJson } = useExportMcp();
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
deleteMcpServer([mcpId]);
}, [deleteMcpServer, mcpId]);
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>{children}</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={showEditModal(mcpId)}>
{t('common.edit')} <PenLine />
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={handleExportMcpJson([mcpId])}>
{t('mcp.export')} <Upload />
</DropdownMenuItem>
<DropdownMenuSeparator />
<ConfirmDeleteDialog onOk={handleDelete}>
<DropdownMenuItem
className="text-state-error"
onSelect={(e) => {
e.preventDefault();
}}
onClick={(e) => {
e.stopPropagation();
}}
>
{t('common.delete')} <Trash2 />
</DropdownMenuItem>
</ConfirmDeleteDialog>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@ -0,0 +1,43 @@
import { ConfirmDeleteDialog } from '@/components/confirm-delete-dialog';
import { RAGFlowTooltip } from '@/components/ui/tooltip';
import { useDeleteMcpServer } from '@/hooks/use-mcp-request';
import { PenLine, Trash2, Upload } from 'lucide-react';
import { MouseEventHandler, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import { UseEditMcpReturnType } from './use-edit-mcp';
import { useExportMcp } from './use-export-mcp';
export function McpOperation({
mcpId,
showEditModal,
}: { mcpId: string } & Pick<UseEditMcpReturnType, 'showEditModal'>) {
const { t } = useTranslation();
const { deleteMcpServer } = useDeleteMcpServer();
const { handleExportMcpJson } = useExportMcp();
const handleDelete: MouseEventHandler<HTMLDivElement> = useCallback(() => {
deleteMcpServer([mcpId]);
}, [deleteMcpServer, mcpId]);
return (
<div className="hidden gap-3 group-hover:flex text-text-secondary">
<RAGFlowTooltip tooltip={t('mcp.export')}>
<Upload
className="size-3 cursor-pointer"
onClick={handleExportMcpJson([mcpId])}
/>
</RAGFlowTooltip>
<RAGFlowTooltip tooltip={t('common.edit')}>
<PenLine
className="size-3 cursor-pointer"
onClick={showEditModal(mcpId)}
/>
</RAGFlowTooltip>
<RAGFlowTooltip tooltip={t('common.delete')}>
<ConfirmDeleteDialog onOk={handleDelete}>
<Trash2 className="size-3 cursor-pointer" />
</ConfirmDeleteDialog>
</RAGFlowTooltip>
</div>
);
}

View File

@ -1,10 +1,11 @@
import { useDeleteMcpServer } from '@/hooks/use-mcp-request';
import { IMcpServer } from '@/interfaces/database/mcp';
import { Trash2, Upload } from 'lucide-react';
import { useCallback, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useExportMcp } from './use-export-mcp';
export function useBulkOperateMCP() {
export function useBulkOperateMCP(mcpList: IMcpServer[]) {
const { t } = useTranslation();
const [selectedList, setSelectedList] = useState<Array<string>>([]);
const { deleteMcpServer } = useDeleteMcpServer();
@ -20,6 +21,13 @@ export function useBulkOperateMCP() {
});
}, []);
const handleSelectAll = useCallback(
(checked: boolean) => {
setSelectedList(() => (checked ? mcpList.map((item) => item.id) : []));
},
[mcpList],
);
const list = [
{
id: 'export',
@ -35,7 +43,14 @@ export function useBulkOperateMCP() {
},
];
return { list, selectedList, handleSelectChange };
return {
list,
selectedList,
handleSelectChange,
handleDelete,
handleExportMcp: handleExportMcpJson(selectedList),
handleSelectAll,
};
}
export type UseBulkOperateMCPReturnType = ReturnType<typeof useBulkOperateMCP>;