fix(usage): restore request ID column visibility

This commit is contained in:
scp-planet
2026-08-10 10:54:39 +08:00
parent 2d0976ac2c
commit 5350b3d98e
4 changed files with 156 additions and 1 deletions
@@ -238,6 +238,24 @@
<span class="text-sm text-gray-600 dark:text-gray-400">{{ formatDateTime(value) }}</span>
</template>
<template #cell-request_id="{ row }">
<div v-if="row.request_id" class="flex max-w-[160px] items-center gap-1.5">
<span class="truncate font-mono text-xs text-gray-500 dark:text-gray-400" :title="row.request_id">
{{ row.request_id }}
</span>
<button
type="button"
class="shrink-0 rounded p-0.5 text-gray-400 transition-colors hover:bg-gray-100 hover:text-gray-600 dark:hover:bg-dark-700 dark:hover:text-gray-300"
:class="copiedRequestId === row.request_id ? 'text-green-500 hover:text-green-500' : ''"
:title="copiedRequestId === row.request_id ? t('keys.copied') : t('keys.copyToClipboard')"
@click="copyRequestId(row.request_id)"
>
<Icon :name="copiedRequestId === row.request_id ? 'check' : 'copy'" size="sm" class="h-3.5 w-3.5" />
</button>
</div>
<span v-else class="text-sm text-gray-400 dark:text-gray-500">-</span>
</template>
<template #cell-user_agent="{ row }">
<span v-if="row.user_agent" class="text-sm text-gray-600 dark:text-gray-400 block max-w-[320px] truncate" :title="row.user_agent">{{ formatUserAgent(row.user_agent) }}</span>
<span v-else class="text-sm text-gray-400 dark:text-gray-500">-</span>
@@ -481,6 +499,7 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { formatDateTime, formatReasoningEffort } from '@/utils/format'
import { formatCacheTokens, formatMultiplier } from '@/utils/formatters'
import { formatTokenPricePerMillion } from '@/utils/usagePricing'
@@ -560,6 +579,8 @@ const emit = defineEmits<{
ipGeoBatchFailed: []
}>()
const { t } = useI18n()
const appStore = useAppStore()
const copiedRequestId = ref<string | null>(null)
const showAccountBilling = props.showAccountBilling
const showUpstreamEndpoint = props.showUpstreamEndpoint
const ipGeoBatchLoading = ref(false)
@@ -609,6 +630,19 @@ const handleBatchFetchIpGeo = async () => {
}
}
const copyRequestId = async (requestId: string) => {
try {
await navigator.clipboard.writeText(requestId)
copiedRequestId.value = requestId
appStore.showSuccess(t('admin.usage.requestIdCopied'))
window.setTimeout(() => {
if (copiedRequestId.value === requestId) copiedRequestId.value = null
}, 2000)
} catch {
appStore.showError(t('common.copyFailed'))
}
}
// Tooltip state - cost
const tooltipVisible = ref(false)
const tooltipPosition = ref({ x: 0, y: 0 })
@@ -4,7 +4,13 @@ const ipGeoMocks = vi.hoisted(() => ({
fetchBatch: vi.fn(),
}))
const appStoreMocks = vi.hoisted(() => ({
showSuccess: vi.fn(),
showError: vi.fn(),
}))
vi.mock('@/utils/ipGeoLookup', () => ipGeoMocks)
vi.mock('@/stores/app', () => ({ useAppStore: () => appStoreMocks }))
import { describe, expect, it, vi, beforeEach } from 'vitest'
import { mount } from '@vue/test-utils'
@@ -51,6 +57,10 @@ const messages: Record<string, string> = {
'admin.usage.billingModeToken': 'Token',
'admin.usage.billingModePerRequest': 'Per request',
'admin.usage.billingModeImage': 'Image',
'admin.usage.requestIdCopied': 'Request ID copied',
'keys.copied': 'Copied',
'keys.copyToClipboard': 'Copy to clipboard',
'common.copyFailed': 'Copy failed',
'usage.requestedModel': 'Requested',
'usage.sentUpstreamModel': 'Sent upstream',
'usage.upstreamResponseModel': 'Upstream response',
@@ -77,6 +87,7 @@ const DataTableStub = {
<slot name="cell-billing_mode" :row="row" />
<slot name="cell-tokens" :row="row" />
<slot name="cell-cost" :row="row" />
<slot name="cell-request_id" :row="row" />
</div>
</div>
`,
@@ -410,6 +421,40 @@ describe('admin UsageTable tooltip', () => {
})
})
describe('admin UsageTable request ID column', () => {
beforeEach(() => {
appStoreMocks.showSuccess.mockReset()
appStoreMocks.showError.mockReset()
})
it('renders and copies the request ID', async () => {
const writeText = vi.fn().mockResolvedValue(undefined)
vi.stubGlobal('navigator', { clipboard: { writeText } })
const wrapper = mount(UsageTable, {
props: {
data: [{ ...baseImageRow, request_id: 'req-admin-visible-id' }],
loading: false,
columns: [{ key: 'request_id', label: 'Request ID' }],
},
global: {
stubs: {
DataTable: DataTableStub,
EmptyState: true,
Icon: true,
Teleport: true,
},
},
})
expect(wrapper.text()).toContain('req-admin-visible-id')
await wrapper.get('button[title="Copy to clipboard"]').trigger('click')
expect(writeText).toHaveBeenCalledWith('req-admin-visible-id')
expect(appStoreMocks.showSuccess).toHaveBeenCalledWith('Request ID copied')
})
})
describe('admin UsageTable IP geolocation batch toolbar', () => {
const DataTableStubWithIp = {
props: ['data'],