mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-05-29 00:09:41 +08:00
跨库SQL智能提示: - 扩展 tablesRef/allColumnsRef 支持跨库元数据存储 - 根据 includeDatabases 配置过滤可见数据库 - 支持三段式(db.table.column)和两段式(db.table)补全格式 - 优化补全权重:FROM表字段优先于其他表和关键字 - 移除数据库类型限制,PostgreSQL等均支持列信息获取 全局透明度与高斯模糊: - 新增 appearance 状态管理(opacity/blur)并持久化 - App/Sidebar/LogPanel/DataGrid/TabManager 适配透明背景 - 使用 backdropFilter 实现高斯模糊效果 - 右键菜单使用 Portal 渲染避免 fixed 定位失效 单元格右键菜单增强: - 合并复制(INSERT/JSON/CSV/Markdown)和导出功能 - 添加 stopPropagation 防止菜单事件冒泡
143 lines
4.3 KiB
TypeScript
143 lines
4.3 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import { Tabs, Dropdown } from 'antd';
|
|
import type { MenuProps } from 'antd';
|
|
import { useStore } from '../store';
|
|
import DataViewer from './DataViewer';
|
|
import QueryEditor from './QueryEditor';
|
|
import TableDesigner from './TableDesigner';
|
|
import RedisViewer from './RedisViewer';
|
|
import RedisCommandEditor from './RedisCommandEditor';
|
|
|
|
const TabManager: React.FC = () => {
|
|
const tabs = useStore(state => state.tabs);
|
|
const activeTabId = useStore(state => state.activeTabId);
|
|
const setActiveTab = useStore(state => state.setActiveTab);
|
|
const closeTab = useStore(state => state.closeTab);
|
|
const closeOtherTabs = useStore(state => state.closeOtherTabs);
|
|
const closeTabsToLeft = useStore(state => state.closeTabsToLeft);
|
|
const closeTabsToRight = useStore(state => state.closeTabsToRight);
|
|
const closeAllTabs = useStore(state => state.closeAllTabs);
|
|
|
|
const onChange = (newActiveKey: string) => {
|
|
setActiveTab(newActiveKey);
|
|
};
|
|
|
|
const onEdit = (targetKey: React.MouseEvent | React.KeyboardEvent | string, action: 'add' | 'remove') => {
|
|
if (action === 'remove') {
|
|
closeTab(targetKey as string);
|
|
}
|
|
};
|
|
|
|
const items = useMemo(() => tabs.map((tab, index) => {
|
|
let content;
|
|
if (tab.type === 'query') {
|
|
content = <QueryEditor tab={tab} />;
|
|
} else if (tab.type === 'table') {
|
|
content = <DataViewer tab={tab} />;
|
|
} else if (tab.type === 'design') {
|
|
content = <TableDesigner tab={tab} />;
|
|
} else if (tab.type === 'redis-keys') {
|
|
content = <RedisViewer connectionId={tab.connectionId} redisDB={tab.redisDB ?? 0} />;
|
|
} else if (tab.type === 'redis-command') {
|
|
content = <RedisCommandEditor connectionId={tab.connectionId} redisDB={tab.redisDB ?? 0} />;
|
|
}
|
|
|
|
const menuItems: MenuProps['items'] = [
|
|
{
|
|
key: 'close-other',
|
|
label: '关闭其他页',
|
|
disabled: tabs.length <= 1,
|
|
onClick: () => closeOtherTabs(tab.id),
|
|
},
|
|
{
|
|
key: 'close-left',
|
|
label: '关闭左侧',
|
|
disabled: index === 0,
|
|
onClick: () => closeTabsToLeft(tab.id),
|
|
},
|
|
{
|
|
key: 'close-right',
|
|
label: '关闭右侧',
|
|
disabled: index === tabs.length - 1,
|
|
onClick: () => closeTabsToRight(tab.id),
|
|
},
|
|
{ type: 'divider' },
|
|
{
|
|
key: 'close-all',
|
|
label: '关闭所有',
|
|
disabled: tabs.length === 0,
|
|
onClick: () => closeAllTabs(),
|
|
},
|
|
];
|
|
|
|
return {
|
|
label: (
|
|
<Dropdown menu={{ items: menuItems }} trigger={['contextMenu']}>
|
|
<span onContextMenu={(e) => e.preventDefault()}>{tab.title}</span>
|
|
</Dropdown>
|
|
),
|
|
key: tab.id,
|
|
children: content,
|
|
};
|
|
}), [tabs, closeOtherTabs, closeTabsToLeft, closeTabsToRight, closeAllTabs]);
|
|
|
|
return (
|
|
<>
|
|
<style>{`
|
|
.main-tabs {
|
|
height: 100%;
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
.main-tabs .ant-tabs-nav {
|
|
flex: 0 0 auto;
|
|
}
|
|
.main-tabs .ant-tabs-content-holder {
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.main-tabs .ant-tabs-content {
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
.main-tabs .ant-tabs-tabpane {
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
overflow: hidden;
|
|
}
|
|
.main-tabs .ant-tabs-tabpane > div {
|
|
flex: 1 1 auto;
|
|
min-height: 0;
|
|
}
|
|
.main-tabs .ant-tabs-tabpane-hidden {
|
|
display: none !important;
|
|
}
|
|
.main-tabs .ant-tabs-nav::before {
|
|
border-bottom: none !important;
|
|
}
|
|
`}</style>
|
|
<Tabs
|
|
className="main-tabs"
|
|
type="editable-card"
|
|
onChange={onChange}
|
|
activeKey={activeTabId || undefined}
|
|
onEdit={onEdit}
|
|
items={items}
|
|
hideAdd
|
|
/>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default TabManager;
|