fix: 查询结果固定布局 - 编辑器/sidebar不被结果挤走 + 结果区可折叠
This commit is contained in:
1 parent
471221fc47
commit
0afc9947fb
1 file changed
+36
-11
@@ -126,6 +126,7 @@ export default function QueriesPage() {
|
|||||||
const [templateSearch, setTemplateSearch] = useState('');
|
const [templateSearch, setTemplateSearch] = useState('');
|
||||||
const [templateCategory, setTemplateCategory] = useState('all');
|
const [templateCategory, setTemplateCategory] = useState('all');
|
||||||
const [showSidebar, setShowSidebar] = useState(true);
|
const [showSidebar, setShowSidebar] = useState(true);
|
||||||
|
const [showResult, setShowResult] = useState(true);
|
||||||
const [showHistory, setShowHistory] = useState(false);
|
const [showHistory, setShowHistory] = useState(false);
|
||||||
const [sortState, setSortState] = useState<SortState>({ column: null, direction: null });
|
const [sortState, setSortState] = useState<SortState>({ column: null, direction: null });
|
||||||
const [rowHeight] = useState(36);
|
const [rowHeight] = useState(36);
|
||||||
@@ -328,9 +329,10 @@ export default function QueriesPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-[calc(100vh-3rem)] gap-0">
|
<div className="flex h-[calc(100vh-3rem)] gap-0">
|
||||||
{/* ====== 主区域 ====== */}
|
{/* ====== 主区域 ====== */}
|
||||||
<div className="flex-1 flex flex-col min-w-0 overflow-y-auto p-5 space-y-4">
|
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
||||||
|
|
||||||
{/* -- 顶栏: 实例+库选择 -- */}
|
{/* -- 顶栏: 实例+库选择 -- */}
|
||||||
|
<div className="shrink-0 px-5 pt-4 pb-2">
|
||||||
<div className="flex items-center gap-3 flex-wrap">
|
<div className="flex items-center gap-3 flex-wrap">
|
||||||
<div className="flex items-center gap-2 flex-1 min-w-0">
|
<div className="flex items-center gap-2 flex-1 min-w-0">
|
||||||
{selectedInstance && dbTypeIcon[dbType] && (
|
{selectedInstance && dbTypeIcon[dbType] && (
|
||||||
@@ -387,9 +389,11 @@ export default function QueriesPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* -- 模板选择器 -- */}
|
{/* -- 模板选择器 -- */}
|
||||||
{showTemplates && (
|
{showTemplates && (
|
||||||
|
<div className="shrink-0 px-5 pb-2">
|
||||||
<div className="dg-card p-3">
|
<div className="dg-card p-3">
|
||||||
<div className="flex items-center gap-2 mb-3 flex-wrap">
|
<div className="flex items-center gap-2 mb-3 flex-wrap">
|
||||||
{templateCategories.map(cat => (
|
{templateCategories.map(cat => (
|
||||||
@@ -424,10 +428,12 @@ export default function QueriesPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* -- 编辑器 + 侧边栏 -- */}
|
{/* -- 编辑器 + 侧边栏 (上半区,不随结果滚动) -- */}
|
||||||
<div className="flex gap-3 flex-1 min-h-0">
|
<div className="shrink-0 px-5 pb-2">
|
||||||
|
<div className="flex gap-3">
|
||||||
{/* Schema 侧边栏 */}
|
{/* Schema 侧边栏 */}
|
||||||
{showSidebar && instanceId && (
|
{showSidebar && instanceId && (
|
||||||
<div className="w-[280px] shrink-0">
|
<div className="w-[280px] shrink-0">
|
||||||
@@ -444,9 +450,9 @@ export default function QueriesPage() {
|
|||||||
|
|
||||||
{/* SQL 编辑器 */}
|
{/* SQL 编辑器 */}
|
||||||
<div className="flex-1 min-w-0 flex flex-col">
|
<div className="flex-1 min-w-0 flex flex-col">
|
||||||
<div className="dg-card overflow-hidden flex-1 flex flex-col">
|
<div className="dg-card overflow-hidden flex flex-col">
|
||||||
<SqlEditor value={sql} onChange={setSql} placeholder={placeholderMap[dbType]}
|
<SqlEditor value={sql} onChange={setSql} placeholder={placeholderMap[dbType]}
|
||||||
language={editorLang} height="240px" onExecute={handleExecute}
|
language={editorLang} height="200px" onExecute={handleExecute}
|
||||||
schema={(dbType === 'mysql' || dbType === 'postgres' || dbType === 'postgresql') ? editorSchema : undefined} />
|
schema={(dbType === 'mysql' || dbType === 'postgres' || dbType === 'postgresql') ? editorSchema : undefined} />
|
||||||
<div className="flex items-center justify-between px-3 py-1.5 bg-[var(--dg-bg-tertiary)] border-t border-[var(--dg-border)] text-[11px] dg-text-muted">
|
<div className="flex items-center justify-between px-3 py-1.5 bg-[var(--dg-bg-tertiary)] border-t border-[var(--dg-border)] text-[11px] dg-text-muted">
|
||||||
<span className="flex items-center gap-1.5">
|
<span className="flex items-center gap-1.5">
|
||||||
@@ -461,22 +467,34 @@ export default function QueriesPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* -- 错误提示 -- */}
|
{/* -- 错误提示 -- */}
|
||||||
{error && (
|
{error && (
|
||||||
|
<div className="shrink-0 px-5 pb-2">
|
||||||
<div className="flex items-center gap-2 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-sm text-red-400">
|
<div className="flex items-center gap-2 p-3 bg-red-500/10 border border-red-500/20 rounded-lg text-sm text-red-400">
|
||||||
<AlertTriangle className="w-4 h-4 shrink-0" />{error}
|
<AlertTriangle className="w-4 h-4 shrink-0" />{error}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* -- 查询结果 -- */}
|
{/* -- 查询结果 (下半区,独立滚动) -- */}
|
||||||
{result && (
|
{result && (
|
||||||
<div className="dg-card overflow-hidden">
|
<div className="flex-1 min-h-0 px-5 pb-4 overflow-hidden flex flex-col">
|
||||||
<div className="border-b border-[var(--dg-border)] py-2.5 px-4">
|
<div className="dg-card overflow-hidden flex-1 flex flex-col">
|
||||||
|
{/* 结果标题栏 + 折叠按钮 */}
|
||||||
|
<div
|
||||||
|
className="shrink-0 border-b border-[var(--dg-border)] py-2 px-4 cursor-pointer hover:bg-[var(--dg-bg-hover)] transition-colors"
|
||||||
|
onClick={() => setShowResult(!showResult)}
|
||||||
|
>
|
||||||
<div className="flex items-center justify-between flex-wrap gap-2">
|
<div className="flex items-center justify-between flex-wrap gap-2">
|
||||||
<span className="text-sm font-semibold flex items-center gap-2">
|
<span className="text-sm font-semibold flex items-center gap-2">
|
||||||
<CheckCircle className="w-4 h-4 text-green-400" />
|
<CheckCircle className="w-4 h-4 text-green-400" />
|
||||||
查询结果
|
查询结果
|
||||||
|
{showResult
|
||||||
|
? <ChevronDown className="w-3.5 h-3.5 text-[var(--dg-text-muted)]" />
|
||||||
|
: <ChevronRight className="w-3.5 h-3.5 text-[var(--dg-text-muted)]" />
|
||||||
|
}
|
||||||
</span>
|
</span>
|
||||||
<div className="flex items-center gap-3 text-xs dg-text-secondary">
|
<div className="flex items-center gap-3 text-xs dg-text-secondary">
|
||||||
<span className="flex items-center gap-1"><Clock className="w-3 h-3" />{result.execution_time_ms || result.cost_ms || 0}ms</span>
|
<span className="flex items-center gap-1"><Clock className="w-3 h-3" />{result.execution_time_ms || result.cost_ms || 0}ms</span>
|
||||||
@@ -490,7 +508,7 @@ export default function QueriesPage() {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{result.rows?.length > 0 && (
|
{result.rows?.length > 0 && (
|
||||||
<button onClick={handleExportResult}
|
<button onClick={(e) => { e.stopPropagation(); handleExportResult(); }}
|
||||||
className="dg-btn dg-btn-ghost dg-btn-sm">
|
className="dg-btn dg-btn-ghost dg-btn-sm">
|
||||||
<Download className="w-3 h-3" />导出 CSV
|
<Download className="w-3 h-3" />导出 CSV
|
||||||
</button>
|
</button>
|
||||||
@@ -498,7 +516,10 @@ export default function QueriesPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="p-0">
|
|
||||||
|
{/* 结果内容(可折叠) */}
|
||||||
|
{showResult && (
|
||||||
|
<div className="flex-1 min-h-0 overflow-auto p-0">
|
||||||
{result.raw_text !== undefined ? (
|
{result.raw_text !== undefined ? (
|
||||||
/* Raw value display (Redis GET/HGET/TTL etc.) */
|
/* Raw value display (Redis GET/HGET/TTL etc.) */
|
||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
@@ -517,7 +538,7 @@ export default function QueriesPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : result.columns && result.columns.length > 0 ? (
|
) : result.columns && result.columns.length > 0 ? (
|
||||||
<div ref={resultContainerRef} onScroll={handleResultScroll} className="overflow-auto max-h-[500px]">
|
<div ref={resultContainerRef} onScroll={handleResultScroll} className="overflow-auto">
|
||||||
<table className="w-full text-sm">
|
<table className="w-full text-sm">
|
||||||
<thead className="sticky top-0 z-10">
|
<thead className="sticky top-0 z-10">
|
||||||
<tr className="dg-table-header">
|
<tr className="dg-table-header">
|
||||||
@@ -602,15 +623,19 @@ export default function QueriesPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* -- 安全提示 -- */}
|
{/* -- 安全提示 -- */}
|
||||||
|
<div className="shrink-0 px-5 pb-3">
|
||||||
<div className="flex items-center gap-2 px-3 py-2 bg-yellow-500/5 border border-yellow-500/15 rounded-lg text-xs text-[var(--dg-text-muted)]">
|
<div className="flex items-center gap-2 px-3 py-2 bg-yellow-500/5 border border-yellow-500/15 rounded-lg text-xs text-[var(--dg-text-muted)]">
|
||||||
<AlertTriangle className="w-3.5 h-3.5 text-yellow-400 shrink-0" />
|
<AlertTriangle className="w-3.5 h-3.5 text-yellow-400 shrink-0" />
|
||||||
查询结果中的敏感字段将自动脱敏。MySQL/PG 仅支持 SELECT 查询,Redis/MongoDB 支持只读命令。
|
查询结果中的敏感字段将自动脱敏。MySQL/PG 仅支持 SELECT 查询,Redis/MongoDB 支持只读命令。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* ====== 右侧历史抽屉 ====== */}
|
{/* ====== 右侧历史抽屉 ====== */}
|
||||||
{showHistory && (
|
{showHistory && (
|
||||||
|
|||||||
Reference in new issue
Block a user