feat(db): 支持 VictoriaMetrics 数据库

* feat(victoriametrics): support native MetricsQL queries

* fix(victoriametrics): 完善指标浏览和结果导出

* fix(victoriametrics): 修复指标对象通用操作入口

* fix(victoriametrics): 修正指标注释和截断提示

* fix(i18n): 补齐 VictoriaMetrics 连接配置翻译

---------

Co-authored-by: zipg <4047349+zipg@users.noreply.github.com>
This commit is contained in:
zipgandzipg authored and GitHub committed 2026-08-05 23:58:11 +08:00
1 parent 930aa48c20
commit 2ede64f1cb
64 files changed
+1293 -64

No files matched your search

@@ -139,7 +139,7 @@ import { BOOLEAN_CELL_EDITOR_VALUES, booleanCellEditorValue, isBooleanCellValue,
import { resolveDataGridColumnsByResultIndex } from "@/lib/dataGrid/dataGridColumnMetadata";
import { isCancelSearchShortcut, isCopyCurrentRowShortcut, isDeleteCurrentRowShortcut, isFocusSearchShortcut, isModRShortcut, isSaveShortcut, isToggleTransposeShortcut } from "@/lib/editor/keyboardShortcuts";
import { dataGridHeaderContentWidth, scrollbarGutterWidth } from "@/lib/dataGrid/dataGridScrollGutter";
import { canFetchNextDataGridSegment, canGoNextDataGridPage, dataGridTotalRowCountLabelKey, hasCompleteLocalDataGridResult, resolveDataGridPaginationTotal, type DataGridInexactTotalRowCountMode } from "@/lib/dataGrid/dataGridPagination";
import { canFetchNextDataGridSegment, canGoNextDataGridPage, dataGridTotalRowCountLabelKey, dataGridTruncationHintKey, hasCompleteLocalDataGridResult, resolveDataGridPaginationTotal, type DataGridInexactTotalRowCountMode } from "@/lib/dataGrid/dataGridPagination";
import { dataGridCountQueryOptions } from "@/lib/dataGrid/dataGridQueryOptions";
import { dataGridBottomScrollTop, dataGridScrollPosition, isDataGridAtScrollBottom, isDataGridNearScrollBottom, isDataGridPrefixAppend, shouldCheckInfiniteScrollAfterScroll, type DataGridScrollPosition } from "@/lib/dataGrid/dataGridInfiniteScroll";
import { CANVAS_DATA_GRID_ROW_HEIGHT, canvasDataGridActionOverlayWidth, canvasDataGridActionReservedWidth, dataGridSearchMatchKey, drawCanvasDataGrid, type CanvasDevicePixelSize } from "@/lib/dataGrid/canvasDataGridRenderer";
@@ -2484,6 +2484,7 @@ watch(
const manualTotalRowCount = ref<number | undefined>(undefined);
const manualTotalRowCountLoading = ref(false);
const showTruncationWarning = computed(() => props.result.truncated === true && typeof props.pageLimit !== "number" && props.result.has_more !== true);
const truncationHintKey = computed(() => dataGridTruncationHintKey(resolvedDatabaseType.value));
const isResultsContext = computed(() => props.context === "results");
// affected_rows reported by the backend can be larger than the rows we
// actually have in memory — e.g. ES auto-pages SELECT * on a big index and
@@ -2613,8 +2614,8 @@ watch(
const showQueryEditReadOnlyBadge = computed(() => isResultsContext.value && hasData.value && !props.editable && !!props.queryEditabilityReason);
const queryEditReadOnlyReason = computed(() => (props.queryEditabilityReason ? t(`grid.queryEditUnsupported.${props.queryEditabilityReason}`) : ""));
const showKeylessEditWarning = computed(() => !!props.editable && !!props.tableMeta && canUseKeylessRowPredicate(props.databaseType, props.tableMeta.primaryKeys ?? []));
const canShowWhereSearch = computed(() => !!props.onExecuteSql && !isResultsContext.value);
const canUseWhereSearch = computed(() => !!props.tableMeta && !!props.onExecuteSql && !isResultsContext.value);
const canShowWhereSearch = computed(() => !!props.onExecuteSql && !isResultsContext.value && resolvedDatabaseType.value !== "victoriametrics");
const canUseWhereSearch = computed(() => !!props.tableMeta && canShowWhereSearch.value);
const canUseServerColumnFilter = computed(() => canUseWhereSearch.value && !!props.connectionId && !!props.tableMeta);
type DataGridTableMeta = NonNullable<typeof props.tableMeta>;
const hiveTableTransactional = ref<boolean | undefined>(undefined);
@@ -8635,7 +8636,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
</div>
<!-- Truncation warning banner -->
<div v-if="showTruncationWarning" class="shrink-0 px-3 py-1 bg-amber-500/10 border-b border-amber-500/20 text-xs text-amber-600 dark:text-amber-400 flex items-center gap-1.5">
<span>{{ t("grid.truncatedHint", { count: result.rows.length }) }}</span>
<span>{{ t(truncationHintKey, { count: result.rows.length }) }}</span>
</div>
<!-- Content area: table + side/bottom detail panes -->
<div class="flex-1 grid min-h-0 overflow-hidden" :style="contentGridStyle">
@@ -1,11 +1,17 @@
import { describe, expect, it } from "vitest";
import { dataGridTotalRowCountLabelKey } from "@/lib/dataGrid/dataGridPagination";
import { dataGridTotalRowCountLabelKey, dataGridTruncationHintKey } from "@/lib/dataGrid/dataGridPagination";
import DataGrid from "../DataGrid.vue";
type VuePropDefinition = { default?: unknown };
type VueComponentWithProps = { props?: Record<string, VuePropDefinition> };
describe("DataGrid total row count exactness", () => {
it("uses a VictoriaMetrics-specific truncation explanation", () => {
expect(dataGridTruncationHintKey("victoriametrics")).toBe("grid.victoriaMetricsTruncatedHint");
expect(dataGridTruncationHintKey("mysql")).toBe("grid.truncatedHint");
expect(dataGridTruncationHintKey()).toBe("grid.truncatedHint");
});
it("treats totals as exact unless a caller explicitly marks them as a lower bound", () => {
const component = DataGrid as unknown as VueComponentWithProps;
expect(component.props?.totalRowCountIsExact?.default).toBe(true);