From ef0bfc100bb8bd148598082e3c915a7f1250986e Mon Sep 17 00:00:00 2001 From: Nixevol Date: Fri, 26 Jun 2026 05:35:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=95=B0=E6=8D=AE=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E8=A1=8C=E6=94=AF=E6=8C=81=E3=80=8C=E6=B8=85?= =?UTF-8?q?=E7=A9=BA=E5=AD=97=E6=AE=B5=3D=E5=86=99=E5=85=A5=20NULL?= =?UTF-8?q?=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/project_context.md | 2 +- frontend/src/components/DatabasePanel.vue | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/project_context.md b/docs/project_context.md index e6be2c9..f02c569 100644 --- a/docs/project_context.md +++ b/docs/project_context.md @@ -893,7 +893,7 @@ 数据管理(`frontend/src/components/DatabasePanel.vue` + `app/api/routers/database.py` + `app/database.py` + `app/warehouse.py`): - 数据表列宽可拖拽:`n-data-table` 每列加 `resizable + width`,`scroll-x` 计入新增操作列宽度。 -- 新增固定右侧「操作」列:每行「编辑 / 删除」。编辑弹窗按列字段逐项填写后保存;删除二次确认;操作后自动刷新当前页。 +- 新增固定右侧「操作」列:每行「编辑 / 删除」。编辑弹窗按列字段逐项填写后保存(清空某字段=写入 NULL,前端空串→`null`,后端 `None`→SQL `NULL`);删除二次确认;操作后自动刷新当前页。 - 行定位策略:以「整行原值」作为 WHERE 条件 + `LIMIT 1`(NULL 用 `IS NULL`),无主键表也能精确改/删单行、避免误伤重复行。后端 `DatabaseManager.update_row/delete_row`(参数化)与 `MetrixWarehouse.update_row/delete_row`(经 `execute_sql` 字面量 SQL)。 - 工具栏新增「模板」「导入」(在 刷新/清空/删除 同组):模板下载当前表字段的 CSV 表头(`POST /api/database/table/template`,FileResponse);导入上传 CSV(`POST /api/database/table/import`,multipart,sync def 走线程池)。**导入按模板校验**:CSV 表头必须与表字段完全一致,缺字段/多字段一律 400 失败;通过后追加导入(`DatabaseManager.import_csv` 走 `bulk_insert`;Metrix 走平台 `/import` mode=append、create_table=false)。 - 新接口均支持 `database_source`(main/cell_data) 双库;前端用 `client.upload`(multipart) 导入、`download`(POST) 下模板。 diff --git a/frontend/src/components/DatabasePanel.vue b/frontend/src/components/DatabasePanel.vue index 2ca3417..dae6d48 100644 --- a/frontend/src/components/DatabasePanel.vue +++ b/frontend/src/components/DatabasePanel.vue @@ -808,8 +808,11 @@ async function saveEditRow() { if (!selectedTable.value || !editRowOriginal.value || savingRow.value) return; savingRow.value = true; try { - const values: Record = {}; - editRowKeys.value.forEach(key => { values[key] = editRowForm[key] ?? ''; }); + const values: Record = {}; + editRowKeys.value.forEach(key => { + const cell = editRowForm[key] ?? ''; + values[key] = cell === '' ? null : cell; + }); await apiPost('/api/database/table/row/update', { table_name: selectedTable.value, database_source: selectedDatabaseSource.value,