From b0833eebf72c6b52eb12ce440b2ed63b375b245f Mon Sep 17 00:00:00 2001 From: xfy Date: Mon, 6 Jul 2026 10:33:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(sql-console):=20=E5=8D=95=E5=85=83?= =?UTF-8?q?=E6=A0=BC=E7=B1=BB=E5=9E=8B=E5=8C=96=E6=B8=B2=E6=9F=93=20(NULL/?= =?UTF-8?q?=E5=B8=83=E5=B0=94/=E6=95=B0=E5=AD=97/=E6=96=87=E6=9C=AC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/sql_result_table.rs | 70 ++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/components/sql_result_table.rs b/src/components/sql_result_table.rs index a9c7081..76eb79c 100644 --- a/src/components/sql_result_table.rs +++ b/src/components/sql_result_table.rs @@ -15,6 +15,76 @@ const EXPAND_MAX_HEIGHT_CLASS: &str = "max-h-80"; /// 文本单元格的列宽上限(Tailwind 任意值,约束长文本不无限拉伸)。 const TEXT_CELL_MAX_WIDTH_CLASS: &str = "max-w-[24rem]"; +/// 渲染数据行内的单个单元格(截断态)。 +/// +/// 按 `serde_json::Value` variant 分发样式:NULL 斜体灰、布尔彩色徽章、 +/// 数字右对齐等宽、文本截断省略。类型信息直接来自后端 `col_to_json` 的编码, +/// 前端无需依赖 PG 类型名。 +fn render_cell(value: &serde_json::Value) -> Element { + use serde_json::Value; + match value { + Value::Null => rsx! { + span { class: "italic text-[var(--color-paper-tertiary)]", "NULL" } + }, + Value::Bool(true) => rsx! { + span { + class: "inline-flex items-center px-1.5 py-0.5 rounded text-xs font-mono", + style: "background-color: var(--color-paper-accent-soft); color: var(--color-paper-accent);", + "true" + } + }, + Value::Bool(false) => rsx! { + span { + class: "inline-flex items-center px-1.5 py-0.5 rounded text-xs font-mono", + style: "background-color: rgb(254 243 199); color: rgb(180 83 9);", + "false" + } + }, + Value::Number(n) => rsx! { + span { class: "block text-right tabular-nums text-[var(--color-paper-primary)]", + "{n}" + } + }, + Value::String(s) => rsx! { + span { + class: "block font-mono text-xs text-[var(--color-paper-secondary)] truncate {TEXT_CELL_MAX_WIDTH_CLASS}", + "{s}" + } + }, + // Array / Object(理论不会出现,col_to_json 不产生复合类型,防御性兜底) + other => rsx! { + span { class: "block font-mono text-xs text-[var(--color-paper-secondary)] truncate {TEXT_CELL_MAX_WIDTH_CLASS}", + "{other}" + } + }, + } +} + +/// 渲染展开详情区中的「列名: 完整值」单行。 +/// +/// 与 `render_cell` 不同:值不做截断,长文本由外层 `
` 的
+/// `whitespace-pre-wrap break-all` + `max-h-80 overflow-y-auto` 承载。
+fn render_expanded_value(col: &str, value: &serde_json::Value) -> Element {
+    use serde_json::Value;
+    let display = match value {
+        Value::Null => "NULL".to_string(),
+        Value::Bool(b) => b.to_string(),
+        Value::Number(n) => n.to_string(),
+        Value::String(s) => s.clone(),
+        other => other.to_string(),
+    };
+    rsx! {
+        div { class: "flex gap-2 py-0.5",
+            span { class: "shrink-0 font-mono text-xs text-[var(--color-paper-tertiary)] min-w-[6rem]",
+                "{col}"
+            }
+            span { class: "font-mono text-xs text-[var(--color-paper-secondary)]",
+                "{display}"
+            }
+        }
+    }
+}
+
 /// SQL 查询结果表格。
 #[derive(Props, Clone, PartialEq)]
 pub struct SqlResultTableProps {