{exportProgressModal}
{isV2Ui &&
}
{isV2Ui && (
{activeConnectionDisplayName}
{activeDatabaseDisplayName || v2NoDatabaseSelectedLabel}
{onCreateConnection && (
}
aria-label={t('connection.new')}
data-gonavi-create-connection-action="true"
onClick={onCreateConnection}
/>
)}
}
aria-label={v2ConnectionActionsLabel}
disabled={!activeConnection}
onClick={(event) => {
if (activeConnection) {
openV2ConnectionContextMenu(event, activeConnection);
}
}}
/>
)}
{isV2Ui && !v2UseLegacySidebarFilter ? (
) : isV2Ui ? (
}
/>
) : (
}
style={{
borderRadius: 6,
border: 'none',
background: darkMode ? 'rgba(0,0,0,0.25)' : 'rgba(0,0,0,0.03)',
boxShadow: 'none',
padding: '4px 8px',
color: darkMode ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.85)',
}}
suffix={(
{
if (!isSearchScopePopoverOpen) {
e.currentTarget.style.background = darkMode ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.04)';
e.currentTarget.style.color = darkMode ? 'rgba(255,255,255,0.85)' : 'rgba(0,0,0,0.65)';
}
}}
onMouseLeave={(e) => {
if (!isSearchScopePopoverOpen) {
e.currentTarget.style.background = 'transparent';
e.currentTarget.style.color = searchScopes.includes('smart')
? (darkMode ? '#ffd666' : '#1677ff')
: (darkMode ? 'rgba(255,255,255,0.45)' : 'rgba(0,0,0,0.45)');
}
}}
>
{searchScopes.includes('smart') ? t('sidebar.command_search.scope.compact_smart') : searchScopes.length}
)}
/>
)}
{isV2Ui && (
{V2_EXPLORER_FILTER_OPTIONS.map((item) => (
))}
)}
{/* Toolbar */}
{!isV2Ui && (
}
aria-label="新建组"
data-sidebar-create-group-action="true"
onClick={() => { setRenameViewTarget(null); createTagForm.resetFields(); setIsCreateTagModalOpen(true); }}
style={{ color: legacyToolbarButtonColor }}
/>
}
aria-label="批量操作表"
data-sidebar-batch-table-action="true"
onClick={() => openBatchTableExportWorkbench()}
style={{ color: legacyToolbarButtonColor }}
/>
}
aria-label="批量操作库"
data-sidebar-batch-database-action="true"
onClick={() => openBatchDatabaseExportWorkbench()}
style={{ color: legacyToolbarButtonColor }}
/>
}
aria-label={v2OpenExternalSqlFileLabel}
data-sidebar-open-external-sql-file-action="true"
onClick={handleOpenSQLFileFromToolbar}
style={{ color: legacyToolbarButtonColor }}
/>
}
aria-label="定位当前标签页"
data-sidebar-locate-current-tab-action="true"
disabled={!canLocateActiveTab}
onClick={handleLocateActiveTabInSidebar}
style={{ color: legacyToolbarButtonColor }}
/>
)}
node.type === 'connection' || node.type === 'tag'
}}
onDragStart={() => {
snapshotTreeSelectionBeforeDrag();
treeDragSelectSuppressUntilRef.current = Date.now() + 600;
setIsTreeDragging(true);
}}
onDragEnter={() => {
treeDragSelectSuppressUntilRef.current = Date.now() + 600;
setIsTreeDragging(true);
}}
onDragEnd={() => {
restoreTreeSelectionAfterDrag();
setIsTreeDragging(false);
}}
onDrop={handleDrop}
loadData={onLoadData}
treeData={isV2Ui ? v2VisibleTreeData : displayTreeData}
onDoubleClick={onDoubleClick}
onSelect={onSelect}
titleRender={titleRender}
expandedKeys={expandedKeys}
onExpand={onExpand}
loadedKeys={loadedKeys}
onLoad={setLoadedKeys}
autoExpandParent={autoExpandParent}
selectedKeys={selectedKeys}
blockNode
height={effectiveTreeHeight}
scrollWidth={isV2Ui ? v2TreeHorizontalScrollWidth : undefined}
onRightClick={onRightClick}
/>
{isV2Ui && (
)}
{contextMenu?.kind && typeof document !== 'undefined' && createPortal(
event.stopPropagation()}
onClick={(event) => event.stopPropagation()}
onContextMenu={(event) => event.preventDefault()}
>
{renderV2SidebarContextMenuContent(contextMenu)}
,
document.body,
)}
{contextMenu && !contextMenu.kind && (
{ if (!open) setContextMenu(null); }}
trigger={['contextMenu']}
rootClassName={contextMenu.rootClassName}
overlayStyle={contextMenu.overlayStyle}
>
)}
,
renameViewTarget?.type === 'tag' ? t('sidebar.modal.tag.edit_title') : t('sidebar.modal.tag.create_title'),
renameViewTarget?.type === 'tag' ? t('sidebar.modal.tag.edit_description') : t('sidebar.modal.tag.create_description')
)}
open={isCreateTagModalOpen}
centered
styles={{ content: modalPanelStyle, header: { background: 'transparent', borderBottom: 'none', paddingBottom: 10 }, body: { paddingTop: 8 }, footer: { background: 'transparent', borderTop: 'none', paddingTop: 12 } }}
onOk={() => {
createTagForm.validateFields().then(values => {
if (renameViewTarget?.type === 'tag') {
// Rename
updateConnectionTag({
...renameViewTarget.dataRef,
name: values.name,
connectionIds: values.connectionIds || []
});
// update cross-connections
const allOtherTagsIds = connectionTags.filter(t => t.id !== renameViewTarget.dataRef.id).flatMap(t => t.connectionIds);
(values.connectionIds || []).forEach((cid: string) => {
if (allOtherTagsIds.includes(cid)) {
moveConnectionToTag(cid, renameViewTarget.dataRef.id);
}
});
} else {
// Create
const tagId = Date.now().toString();
addConnectionTag({
id: tagId,
name: values.name,
connectionIds: values.connectionIds || []
});
(values.connectionIds || []).forEach((cid: string) => {
moveConnectionToTag(cid, tagId);
});
}
setIsCreateTagModalOpen(false);
});
}}
onCancel={() => setIsCreateTagModalOpen(false)}
>
setIsCreateDbModalOpen(false)}
>
{/* Charset option could be added here */}
{
setIsCreateSchemaModalOpen(false);
setCreateSchemaTarget(null);
createSchemaForm.resetFields();
}}
>
{
setIsRenameSchemaModalOpen(false);
setRenameSchemaTarget(null);
renameSchemaForm.resetFields();
}}
>
{
setIsRenameDbModalOpen(false);
setRenameDbTarget(null);
renameDbForm.resetFields();
}}
>
{
setIsRenameTableModalOpen(false);
setRenameTableTarget(null);
renameTableForm.resetFields();
}}
>
{
setIsRenameViewModalOpen(false);
setRenameViewTarget(null);
renameViewForm.resetFields();
}}
>
{
setIsRenameSavedQueryModalOpen(false);
setRenameSavedQueryTarget(null);
renameSavedQueryForm.resetFields();
}}
okText={t('query_editor.action.rename_query')}
cancelText={t('common.cancel')}
>
{
setIsExternalSQLFileModalOpen(false);
setExternalSQLFileTarget(null);
externalSQLFileForm.resetFields();
}}
okText={t(externalSQLFileModalMode === 'create' || externalSQLFileModalMode === 'create-directory' ? 'sidebar.external_sql_modal.action.create' : 'sidebar.external_sql_modal.action.rename')}
cancelText={t('common.cancel')}
>
{
const name = String(value || '').trim();
if (!name) return;
if (/[\\/]/.test(name) || name === '.' || name === '..') {
throw new Error(isExternalSQLDirectoryModalMode(externalSQLFileModalMode) ? t('sidebar.external_sql_modal.validation.directory_name_no_separator') : t('sidebar.external_sql_modal.validation.sql_file_name_no_separator'));
}
},
},
]}
extra={isExternalSQLDirectoryModalMode(externalSQLFileModalMode) ? t('sidebar.external_sql_modal.help.directory') : t('sidebar.external_sql_modal.help.sql_file')}
>
, "批量操作表", "按对象批量导出结构、数据或完整备份。")}
open={isBatchModalOpen}
onCancel={() => setIsBatchModalOpen(false)}
width={720}
centered
styles={{ content: modalPanelStyle, header: { background: 'transparent', borderBottom: 'none', paddingBottom: 10 }, body: { paddingTop: 8 }, footer: { background: 'transparent', borderTop: 'none', paddingTop: 12 } }}
footer={
}
onClick={() => handleBatchClear()}
disabled={checkedTableKeys.length === 0}
>
清空表
}
onClick={() => handleBatchExport('schema')}
disabled={checkedTableKeys.length === 0}
>
导出结构
}
onClick={() => handleBatchExport('dataOnly')}
disabled={checkedTableKeys.length === 0}
>
仅数据(INSERT)
}
onClick={() => handleBatchExport('backup')}
disabled={checkedTableKeys.length === 0}
>
备份(结构+数据)
}
>
先选择连接与数据库,再决定导出范围和目标对象。
{batchTables.length > 0 && (
setBatchFilterKeyword(e.target.value)}
placeholder="筛选表/视图名称"
prefix={}
style={{ width: 260 }}
/>
当前筛选命中 {filteredBatchObjects.length} / {batchTables.length} 个对象
)}
{batchTables.length > 0 && (
<>
已选择 {checkedTableKeys.length} / {batchTables.length} 个对象
setCheckedTableKeys(values as string[])}
style={{ width: '100%' }}
>
{groupedBatchObjects.tables.length > 0 && (
表 ({groupedBatchObjects.tables.length})
{groupedBatchObjects.tables.map(table => (
{table.title}
))}
)}
{groupedBatchObjects.views.length > 0 && (
视图 ({groupedBatchObjects.views.length})
{groupedBatchObjects.views.map(view => (
{view.title}
))}
)}
{groupedBatchObjects.tables.length === 0 && groupedBatchObjects.views.length === 0 && (
无匹配对象
)}
>
)}
, "批量操作库", "按数据库批量导出结构,或生成结构加数据的备份。")}
open={isBatchDbModalOpen}
onCancel={() => setIsBatchDbModalOpen(false)}
width={640}
centered
styles={{ content: modalPanelStyle, header: { background: 'transparent', borderBottom: 'none', paddingBottom: 10 }, body: { paddingTop: 8 }, footer: { background: 'transparent', borderTop: 'none', paddingTop: 12 } }}
footer={[
,
}
onClick={() => handleBatchDbExport(false)}
disabled={checkedDbKeys.length === 0}
>
导出库结构 ({checkedDbKeys.length})
,
}
onClick={() => handleBatchDbExport(true)}
disabled={checkedDbKeys.length === 0}
>
备份库 ({checkedDbKeys.length})
]}
>
{connections.filter(c => c.config.type !== 'redis').map(conn => (
{conn.name}
))}
连接选定后会加载当前连接下可批量导出的数据库列表。
{batchDatabases.length > 0 && (
<>
已选择 {checkedDbKeys.length} / {batchDatabases.length} 个库
setCheckedDbKeys(values as string[])}
style={{ width: '100%' }}
>
{batchDatabases.map(db => (
{db.title}
))}
>
)}
{/* SQL 文件流式执行进度 Modal */}
{
CancelSQLFileExecution(sqlFileExecState.jobId);
setSqlFileExecState(prev => ({ ...prev, status: 'cancelled' }));
},
onClose: () => setSqlFileExecState(prev => ({ ...prev, open: false })),
})}
onCancel={() => {
if (sqlFileExecState.status !== 'running') {
setSqlFileExecState(prev => ({ ...prev, open: false }));
}
}}
styles={{ content: modalPanelStyle, header: { background: 'transparent', borderBottom: 'none' }, body: { paddingTop: 8 }, footer: { background: 'transparent', borderTop: 'none' } }}
>
setFindInDbContext({ open: false, connectionId: '', dbName: '' })}
connectionId={findInDbContext.connectionId}
dbName={findInDbContext.dbName}
/>
setMessagePublishTarget(null)}
onSuccess={handleMessagePublishSuccess}
/>
);
});
export default Sidebar;