feat: OpsxDB 桌面数据库客户端首版入库

含工作台/连库多引擎字段与 SSH·SSL、结果编辑、Redis 键浏览、
Tauri 桌面与 docs/落地页;排除本地构建产物与 sidecar 二进制。
This commit is contained in:
logan committed 2026-09-19 22:43:45 +08:00
commit f8740d27f7
240 files changed
+21625

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
import { useWorkspaceStore } from "./store";
export function SchemaPane() {
const schema = useWorkspaceStore((s) => s.schemaView);
const set = useWorkspaceStore((s) => s.set);
if (!schema) {
return (
<div className="ops-empty" style={{ minHeight: 120 }}>
<h2 style={{ fontSize: "var(--ops-font-sm)" }}>表结构</h2>
<p>在对象树点表,或右键「查看结构」。</p>
</div>
);
}
if (schema.loading) {
return (
<div className="px-3 py-2 text-[12.5px]" style={{ color: "var(--ops-muted)" }}>
加载 {schema.table} 结构…
</div>
);
}
if (schema.error) {
return (
<div
className="m-1.5 rounded border px-2.5 py-1.5 text-[12.5px]"
style={{
borderColor: "color-mix(in srgb, var(--ops-danger) 40%, var(--ops-border))",
background: "color-mix(in srgb, var(--ops-danger) 10%, transparent)",
color: "var(--ops-danger)",
}}
>
{schema.error}
</div>
);
}
return (
<div className="flex min-h-0 flex-1 flex-col">
<div className="ops-section-label justify-between border-b">
<span>
结构 · {schema.database ? `${schema.database}.` : ""}
{schema.table}
</span>
<button
type="button"
className="normal-case tracking-normal hover:underline"
style={{ color: "var(--ops-accent)" }}
onClick={() => set("resultTab", "data")}
>
回结果
</button>
</div>
<div className="min-h-0 flex-1 overflow-auto">
{schema.columns.length === 0 ? (
<div className="px-3 py-2 text-[12.5px]" style={{ color: "var(--ops-muted)" }}>
无列信息
</div>
) : (
<table className="ops-grid" style={{ width: "100%" }}>
<thead>
<tr>
<th style={{ width: 36 }}>#</th>
<th>列名</th>
<th>类型</th>
<th style={{ width: 64 }}>可空</th>
<th>默认值</th>
</tr>
</thead>
<tbody>
{schema.columns.map((c, i) => (
<tr key={c.name}>
<td className="ops-rownum">{i + 1}</td>
<td>
{c.key === "PRI" ? <span title="主键">🔑 </span> : null}
{c.name}
</td>
<td className="font-mono text-[12px]">{c.type}</td>
<td>{c.nullable ? "YES" : "NO"}</td>
<td className="font-mono text-[12px]" style={{ color: "var(--ops-muted)" }}>
{c.default == null ? <span className="ops-null">NULL</span> : String(c.default)}
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
);
}