diff --git a/apps/admin-web/src/pages/AdminDatabaseTablesPage.tsx b/apps/admin-web/src/pages/AdminDatabaseTablesPage.tsx index 91f38076..62e5eacc 100644 --- a/apps/admin-web/src/pages/AdminDatabaseTablesPage.tsx +++ b/apps/admin-web/src/pages/AdminDatabaseTablesPage.tsx @@ -28,6 +28,7 @@ export function AdminDatabaseTablesPage({ const [result, setResult] = useState(null); const [detailRow, setDetailRow] = useState(null); const [errorMessage, setErrorMessage] = useState(''); + const [copyMessage, setCopyMessage] = useState(''); const [isLoadingTables, setIsLoadingTables] = useState(false); const [isLoadingRows, setIsLoadingRows] = useState(false); @@ -91,20 +92,31 @@ export function AdminDatabaseTablesPage({ } } - async function refreshRows(nextTableName = tableName) { + async function refreshRows( + nextTableName = tableName, + options: { + search?: string; + filters?: string; + limit?: string; + } = {}, + ) { const normalizedTableName = nextTableName.trim(); if (!normalizedTableName) { return; } + const querySearch = options.search ?? search; + const queryFilters = options.filters ?? filters; + const queryLimit = options.limit ?? limit; setIsLoadingRows(true); setErrorMessage(''); try { const response = await getAdminDatabaseTableRows(token, normalizedTableName, { - search, - filters, - limit: parseLimit(limit), + search: querySearch, + filters: queryFilters, + limit: parseLimit(queryLimit), }); setResult(response); + setCopyMessage(''); } catch (error: unknown) { handlePageError(error, onUnauthorized, setErrorMessage); } finally { @@ -125,6 +137,27 @@ export function AdminDatabaseTablesPage({ } } + function handleResetQuery() { + setSearch(''); + setFilters(''); + setLimit('100'); + void refreshRows(tableName, {search: '', filters: '', limit: '100'}); + } + + async function handleCopyDetailJson() { + if (!detailRow) { + return; + } + + const copiedText = JSON.stringify(detailRow.raw ?? detailRow.cells, null, 2); + try { + await navigator.clipboard.writeText(copiedText); + setCopyMessage('已复制 JSON'); + } catch { + setCopyMessage('复制失败,请手动选中后复制'); + } + } + return (
@@ -201,6 +234,20 @@ export function AdminDatabaseTablesPage({ {isLoadingRows ? '查询中' : '查询'}
+
+ +
+ 已选表:{tableName || '-'} + 显示列:{visibleColumns.length} +
+
{errorMessage ? ( @@ -265,17 +312,27 @@ export function AdminDatabaseTablesPage({

行详情

- +
+ + +
+ {copyMessage ?
{copyMessage}
: null}
-              {JSON.stringify(detailRow.cells, null, 2)}
+              {JSON.stringify(detailRow.raw ?? detailRow.cells, null, 2)}
             
diff --git a/apps/admin-web/src/styles/admin.css b/apps/admin-web/src/styles/admin.css index faf13802..6152ff73 100644 --- a/apps/admin-web/src/styles/admin.css +++ b/apps/admin-web/src/styles/admin.css @@ -327,10 +327,34 @@ button:disabled { .admin-action-row { display: flex; flex-wrap: wrap; + align-items: center; justify-content: flex-end; gap: 10px; } +.admin-query-action-row { + justify-content: space-between; +} + +.admin-query-summary, +.admin-detail-actions { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.admin-query-summary { + color: #667682; + font-size: 12px; + font-weight: 650; +} + +.admin-query-reset-button { + width: auto; + padding: 0 12px; +} + .admin-field { display: grid; min-width: 0; diff --git a/docs/technical/ADMIN_DATABASE_TABLE_QUERY_2026-05-08.md b/docs/technical/ADMIN_DATABASE_TABLE_QUERY_2026-05-08.md index ede97600..3526ad9d 100644 --- a/docs/technical/ADMIN_DATABASE_TABLE_QUERY_2026-05-08.md +++ b/docs/technical/ADMIN_DATABASE_TABLE_QUERY_2026-05-08.md @@ -11,6 +11,12 @@ - 提供基础查询能力:表选择、关键词搜索、JSON 条件过滤、条数限制、刷新、查看行详情。 - 不修改 SpacetimeDB 表结构,不新增 reducer,不引入写操作。 +## 后续增强 + +- 查询页增加“重置条件”快捷操作,便于运营快速回到默认筛选状态。 +- 行详情支持一键复制完整 JSON,减少人工选中复制的操作成本。 +- 查询页顶部增加轻量摘要,显示当前选表和可见列数,方便移动端快速确认上下文。 + ## 后端接口 ### `GET /admin/api/database/tables`