feat(frontend): 完善ops监控面板和组件功能
This commit is contained in:
@@ -54,6 +54,7 @@ export type OpsUpstreamErrorEvent = {
|
|||||||
account_name?: string
|
account_name?: string
|
||||||
upstream_status_code?: number
|
upstream_status_code?: number
|
||||||
upstream_request_id?: string
|
upstream_request_id?: string
|
||||||
|
upstream_request_body?: string
|
||||||
kind?: string
|
kind?: string
|
||||||
message?: string
|
message?: string
|
||||||
detail?: string
|
detail?: string
|
||||||
@@ -944,7 +945,9 @@ export async function getErrorDistribution(
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listErrorLogs(params: {
|
export type OpsErrorListView = 'errors' | 'excluded' | 'all'
|
||||||
|
|
||||||
|
export type OpsErrorListQueryParams = {
|
||||||
page?: number
|
page?: number
|
||||||
page_size?: number
|
page_size?: number
|
||||||
time_range?: string
|
time_range?: string
|
||||||
@@ -958,10 +961,14 @@ export async function listErrorLogs(params: {
|
|||||||
error_owner?: string
|
error_owner?: string
|
||||||
error_source?: string
|
error_source?: string
|
||||||
resolved?: string
|
resolved?: string
|
||||||
|
view?: OpsErrorListView
|
||||||
|
|
||||||
q?: string
|
q?: string
|
||||||
status_codes?: string
|
status_codes?: string
|
||||||
}): Promise<OpsErrorLogsResponse> {
|
}
|
||||||
|
|
||||||
|
// Legacy unified endpoints
|
||||||
|
export async function listErrorLogs(params: OpsErrorListQueryParams): Promise<OpsErrorLogsResponse> {
|
||||||
const { data } = await apiClient.get<OpsErrorLogsResponse>('/admin/ops/errors', { params })
|
const { data } = await apiClient.get<OpsErrorLogsResponse>('/admin/ops/errors', { params })
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
@@ -985,6 +992,50 @@ export async function updateErrorResolved(errorId: number, resolved: boolean): P
|
|||||||
await apiClient.put(`/admin/ops/errors/${errorId}/resolve`, { resolved })
|
await apiClient.put(`/admin/ops/errors/${errorId}/resolve`, { resolved })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New split endpoints
|
||||||
|
export async function listRequestErrors(params: OpsErrorListQueryParams): Promise<OpsErrorLogsResponse> {
|
||||||
|
const { data } = await apiClient.get<OpsErrorLogsResponse>('/admin/ops/request-errors', { params })
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function listUpstreamErrors(params: OpsErrorListQueryParams): Promise<OpsErrorLogsResponse> {
|
||||||
|
const { data } = await apiClient.get<OpsErrorLogsResponse>('/admin/ops/upstream-errors', { params })
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRequestErrorDetail(id: number): Promise<OpsErrorDetail> {
|
||||||
|
const { data } = await apiClient.get<OpsErrorDetail>(`/admin/ops/request-errors/${id}`)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getUpstreamErrorDetail(id: number): Promise<OpsErrorDetail> {
|
||||||
|
const { data } = await apiClient.get<OpsErrorDetail>(`/admin/ops/upstream-errors/${id}`)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function retryRequestErrorClient(id: number): Promise<OpsRetryResult> {
|
||||||
|
const { data } = await apiClient.post<OpsRetryResult>(`/admin/ops/request-errors/${id}/retry-client`, {})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function retryRequestErrorUpstreamEvent(id: number, idx: number): Promise<OpsRetryResult> {
|
||||||
|
const { data } = await apiClient.post<OpsRetryResult>(`/admin/ops/request-errors/${id}/upstream-errors/${idx}/retry`, {})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function retryUpstreamError(id: number): Promise<OpsRetryResult> {
|
||||||
|
const { data } = await apiClient.post<OpsRetryResult>(`/admin/ops/upstream-errors/${id}/retry`, {})
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateRequestErrorResolved(errorId: number, resolved: boolean): Promise<void> {
|
||||||
|
await apiClient.put(`/admin/ops/request-errors/${errorId}/resolve`, { resolved })
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function updateUpstreamErrorResolved(errorId: number, resolved: boolean): Promise<void> {
|
||||||
|
await apiClient.put(`/admin/ops/upstream-errors/${errorId}/resolve`, { resolved })
|
||||||
|
}
|
||||||
|
|
||||||
export async function listRequestDetails(params: OpsRequestDetailsParams): Promise<OpsRequestDetailsResponse> {
|
export async function listRequestDetails(params: OpsRequestDetailsParams): Promise<OpsRequestDetailsResponse> {
|
||||||
const { data } = await apiClient.get<OpsRequestDetailsResponse>('/admin/ops/requests', { params })
|
const { data } = await apiClient.get<OpsRequestDetailsResponse>('/admin/ops/requests', { params })
|
||||||
return data
|
return data
|
||||||
@@ -1103,11 +1154,25 @@ export const opsAPI = {
|
|||||||
getAccountAvailabilityStats,
|
getAccountAvailabilityStats,
|
||||||
getRealtimeTrafficSummary,
|
getRealtimeTrafficSummary,
|
||||||
subscribeQPS,
|
subscribeQPS,
|
||||||
|
|
||||||
|
// Legacy unified endpoints
|
||||||
listErrorLogs,
|
listErrorLogs,
|
||||||
getErrorLogDetail,
|
getErrorLogDetail,
|
||||||
retryErrorRequest,
|
retryErrorRequest,
|
||||||
listRetryAttempts,
|
listRetryAttempts,
|
||||||
updateErrorResolved,
|
updateErrorResolved,
|
||||||
|
|
||||||
|
// New split endpoints
|
||||||
|
listRequestErrors,
|
||||||
|
listUpstreamErrors,
|
||||||
|
getRequestErrorDetail,
|
||||||
|
getUpstreamErrorDetail,
|
||||||
|
retryRequestErrorClient,
|
||||||
|
retryRequestErrorUpstreamEvent,
|
||||||
|
retryUpstreamError,
|
||||||
|
updateRequestErrorResolved,
|
||||||
|
updateUpstreamErrorResolved,
|
||||||
|
|
||||||
listRequestDetails,
|
listRequestDetails,
|
||||||
listAlertRules,
|
listAlertRules,
|
||||||
createAlertRule,
|
createAlertRule,
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
@openErrorDetail="openError"
|
@openErrorDetail="openError"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<OpsErrorDetailModal v-model:show="showErrorModal" :error-id="selectedErrorId" />
|
<OpsErrorDetailModal v-model:show="showErrorModal" :error-id="selectedErrorId" :error-type="errorDetailsType" />
|
||||||
|
|
||||||
<OpsRequestDetailsModal
|
<OpsRequestDetailsModal
|
||||||
v-model="showRequestDetails"
|
v-model="showRequestDetails"
|
||||||
|
|||||||
@@ -247,11 +247,11 @@
|
|||||||
{{ t('admin.ops.errorDetail.retryClient') }}
|
{{ t('admin.ops.errorDetail.retryClient') }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
v-if="props.errorType === 'upstream'"
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-secondary btn-sm"
|
class="btn btn-secondary btn-sm"
|
||||||
:disabled="retrying || !pinnedAccountId"
|
:disabled="retrying"
|
||||||
@click="openRetryConfirm('upstream')"
|
@click="openRetryConfirm('upstream')"
|
||||||
:title="pinnedAccountId ? '' : t('admin.ops.errorDetail.retryUpstreamHint')"
|
|
||||||
>
|
>
|
||||||
{{ t('admin.ops.errorDetail.retryUpstream') }}
|
{{ t('admin.ops.errorDetail.retryUpstream') }}
|
||||||
</button>
|
</button>
|
||||||
@@ -263,12 +263,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-3">
|
<div v-if="props.errorType === 'upstream'" class="mt-4 grid grid-cols-1 gap-4 md:grid-cols-3">
|
||||||
<div class="md:col-span-1">
|
<div class="md:col-span-1">
|
||||||
<label class="mb-1 block text-xs font-bold uppercase tracking-wider text-gray-400">{{ t('admin.ops.errorDetail.pinnedAccountId') }}</label>
|
<label class="mb-1 block text-xs font-bold uppercase tracking-wider text-gray-400">{{ t('admin.ops.errorDetail.pinnedAccountId') }}</label>
|
||||||
<input v-model="pinnedAccountIdInput" type="text" class="input font-mono text-sm" :placeholder="t('admin.ops.errorDetail.pinnedAccountIdHint')" />
|
<input v-model="pinnedAccountIdInput" type="text" class="input font-mono text-sm" disabled />
|
||||||
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
<div class="mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||||
{{ t('admin.ops.errorDetail.retryNote2') }}
|
pinned to original account_id
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
@@ -327,8 +327,20 @@
|
|||||||
<div class="text-xs font-black text-gray-800 dark:text-gray-100">
|
<div class="text-xs font-black text-gray-800 dark:text-gray-100">
|
||||||
#{{ idx + 1 }} <span v-if="ev.kind" class="font-mono">{{ ev.kind }}</span>
|
#{{ idx + 1 }} <span v-if="ev.kind" class="font-mono">{{ ev.kind }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="font-mono text-xs text-gray-500 dark:text-gray-400">
|
<div class="flex items-center gap-2">
|
||||||
{{ ev.at_unix_ms ? formatDateTime(new Date(ev.at_unix_ms)) : '' }}
|
<button
|
||||||
|
v-if="props.errorType !== 'upstream'"
|
||||||
|
type="button"
|
||||||
|
class="rounded-md bg-gray-100 px-2 py-1 text-[10px] font-bold text-gray-700 hover:bg-gray-200 dark:bg-dark-700 dark:text-gray-200 dark:hover:bg-dark-600"
|
||||||
|
:disabled="retrying || !ev.upstream_request_body"
|
||||||
|
:title="ev.upstream_request_body ? '' : 'missing upstream request body'"
|
||||||
|
@click.stop="retryUpstreamEvent(idx)"
|
||||||
|
>
|
||||||
|
{{ t('admin.ops.errorDetail.retryUpstream') }} #{{ idx + 1 }}
|
||||||
|
</button>
|
||||||
|
<div class="font-mono text-xs text-gray-500 dark:text-gray-400">
|
||||||
|
{{ ev.at_unix_ms ? formatDateTime(new Date(ev.at_unix_ms)) : '' }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -526,13 +538,14 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import BaseDialog from '@/components/common/BaseDialog.vue'
|
import BaseDialog from '@/components/common/BaseDialog.vue'
|
||||||
import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
|
import ConfirmDialog from '@/components/common/ConfirmDialog.vue'
|
||||||
import { useAppStore } from '@/stores'
|
import { useAppStore } from '@/stores'
|
||||||
import { opsAPI, type OpsErrorDetail, type OpsRetryMode, type OpsRetryAttempt } from '@/api/admin/ops'
|
import { opsAPI, type OpsErrorDetail, type OpsRetryAttempt } from '@/api/admin/ops'
|
||||||
import { formatDateTime } from '@/utils/format'
|
import { formatDateTime } from '@/utils/format'
|
||||||
import { getSeverityClass } from '../utils/opsFormatters'
|
import { getSeverityClass } from '../utils/opsFormatters'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
show: boolean
|
show: boolean
|
||||||
errorId: number | null
|
errorId: number | null
|
||||||
|
errorType?: 'request' | 'upstream'
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
@@ -552,7 +565,7 @@ const activeTab = ref<'overview' | 'retries' | 'request' | 'response'>('overview
|
|||||||
|
|
||||||
const retrying = ref(false)
|
const retrying = ref(false)
|
||||||
const showRetryConfirm = ref(false)
|
const showRetryConfirm = ref(false)
|
||||||
const pendingRetryMode = ref<OpsRetryMode>('client')
|
const pendingRetryMode = ref<'client' | 'upstream' | 'upstream_event'>('client')
|
||||||
|
|
||||||
const forceRetryAck = ref(false)
|
const forceRetryAck = ref(false)
|
||||||
const retryHistory = ref<OpsRetryAttempt[]>([])
|
const retryHistory = ref<OpsRetryAttempt[]>([])
|
||||||
@@ -563,12 +576,6 @@ const compareA = ref<number | null>(null)
|
|||||||
const compareB = ref<number | null>(null)
|
const compareB = ref<number | null>(null)
|
||||||
|
|
||||||
const pinnedAccountIdInput = ref('')
|
const pinnedAccountIdInput = ref('')
|
||||||
const pinnedAccountId = computed<number | null>(() => {
|
|
||||||
const raw = String(pinnedAccountIdInput.value || '').trim()
|
|
||||||
if (!raw) return null
|
|
||||||
const n = Number.parseInt(raw, 10)
|
|
||||||
return Number.isFinite(n) && n > 0 ? n : null
|
|
||||||
})
|
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
if (!props.errorId) return 'Error Detail'
|
if (!props.errorId) return 'Error Detail'
|
||||||
@@ -584,6 +591,7 @@ type UpstreamErrorEvent = {
|
|||||||
account_name?: string
|
account_name?: string
|
||||||
upstream_status_code?: number
|
upstream_status_code?: number
|
||||||
upstream_request_id?: string
|
upstream_request_id?: string
|
||||||
|
upstream_request_body?: string
|
||||||
kind?: string
|
kind?: string
|
||||||
message?: string
|
message?: string
|
||||||
detail?: string
|
detail?: string
|
||||||
@@ -641,15 +649,12 @@ const handlingSuggestion = computed(() => {
|
|||||||
async function fetchDetail(id: number) {
|
async function fetchDetail(id: number) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const d = await opsAPI.getErrorLogDetail(id)
|
const kind = props.errorType || (detail.value?.phase === 'upstream' ? 'upstream' : 'request')
|
||||||
|
const d = kind === 'upstream' ? await opsAPI.getUpstreamErrorDetail(id) : await opsAPI.getRequestErrorDetail(id)
|
||||||
detail.value = d
|
detail.value = d
|
||||||
|
|
||||||
// Default pinned account from error log if present.
|
// Keep showing original account_id (read-only hint for upstream retries).
|
||||||
if (d.account_id && d.account_id > 0) {
|
pinnedAccountIdInput.value = d.account_id && d.account_id > 0 ? String(d.account_id) : ''
|
||||||
pinnedAccountIdInput.value = String(d.account_id)
|
|
||||||
} else {
|
|
||||||
pinnedAccountIdInput.value = ''
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
detail.value = null
|
detail.value = null
|
||||||
appStore.showError(err?.message || t('admin.ops.failedToLoadErrorDetail'))
|
appStore.showError(err?.message || t('admin.ops.failedToLoadErrorDetail'))
|
||||||
@@ -679,7 +684,7 @@ watch(
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
function openRetryConfirm(mode: OpsRetryMode) {
|
function openRetryConfirm(mode: 'client' | 'upstream' | 'upstream_event') {
|
||||||
pendingRetryMode.value = mode
|
pendingRetryMode.value = mode
|
||||||
// Force-ack required only when backend says not retryable.
|
// Force-ack required only when backend says not retryable.
|
||||||
forceRetryAck.value = false
|
forceRetryAck.value = false
|
||||||
@@ -733,7 +738,12 @@ const responseTabHint = computed(() => {
|
|||||||
async function markResolved(resolved: boolean) {
|
async function markResolved(resolved: boolean) {
|
||||||
if (!props.errorId) return
|
if (!props.errorId) return
|
||||||
try {
|
try {
|
||||||
await opsAPI.updateErrorResolved(props.errorId, resolved)
|
const kind = props.errorType || (detail.value?.phase === 'upstream' ? 'upstream' : 'request')
|
||||||
|
if (kind === 'upstream') {
|
||||||
|
await opsAPI.updateUpstreamErrorResolved(props.errorId, resolved)
|
||||||
|
} else {
|
||||||
|
await opsAPI.updateRequestErrorResolved(props.errorId, resolved)
|
||||||
|
}
|
||||||
await fetchDetail(props.errorId)
|
await fetchDetail(props.errorId)
|
||||||
appStore.showSuccess(resolved ? (t('admin.ops.errorDetails.resolved') || 'Resolved') : (t('admin.ops.errorDetails.unresolved') || 'Unresolved'))
|
appStore.showSuccess(resolved ? (t('admin.ops.errorDetails.resolved') || 'Resolved') : (t('admin.ops.errorDetails.unresolved') || 'Unresolved'))
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -779,12 +789,20 @@ async function runConfirmedRetry() {
|
|||||||
|
|
||||||
retrying.value = true
|
retrying.value = true
|
||||||
try {
|
try {
|
||||||
const req =
|
const kind = props.errorType || (detail.value?.phase === 'upstream' ? 'upstream' : 'request')
|
||||||
mode === 'upstream'
|
|
||||||
? { mode, pinned_account_id: pinnedAccountId.value ?? undefined, force: !retryable ? true : undefined }
|
let res
|
||||||
: { mode, force: !retryable ? true : undefined }
|
if (kind === 'upstream') {
|
||||||
|
// Upstream error retries always pin the original account_id.
|
||||||
|
res = await opsAPI.retryUpstreamError(props.errorId)
|
||||||
|
} else {
|
||||||
|
if (mode === 'client') {
|
||||||
|
res = await opsAPI.retryRequestErrorClient(props.errorId)
|
||||||
|
} else {
|
||||||
|
throw new Error('Unsupported retry mode')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const res = await opsAPI.retryErrorRequest(props.errorId, req)
|
|
||||||
const summary = res.status === 'succeeded' ? t('admin.ops.errorDetail.retrySuccess') : t('admin.ops.errorDetail.retryFailed')
|
const summary = res.status === 'succeeded' ? t('admin.ops.errorDetail.retrySuccess') : t('admin.ops.errorDetail.retryFailed')
|
||||||
appStore.showSuccess(summary)
|
appStore.showSuccess(summary)
|
||||||
|
|
||||||
@@ -798,6 +816,22 @@ async function runConfirmedRetry() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function retryUpstreamEvent(idx: number) {
|
||||||
|
if (!props.errorId) return
|
||||||
|
try {
|
||||||
|
retrying.value = true
|
||||||
|
const res = await opsAPI.retryRequestErrorUpstreamEvent(props.errorId, idx)
|
||||||
|
const summary = res.status === 'succeeded' ? t('admin.ops.errorDetail.retrySuccess') : t('admin.ops.errorDetail.retryFailed')
|
||||||
|
appStore.showSuccess(summary)
|
||||||
|
await fetchDetail(props.errorId)
|
||||||
|
await loadRetryHistory()
|
||||||
|
} catch (err: any) {
|
||||||
|
appStore.showError(err?.message || t('admin.ops.retryFailed'))
|
||||||
|
} finally {
|
||||||
|
retrying.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function cancelRetry() {
|
function cancelRetry() {
|
||||||
showRetryConfirm.value = false
|
showRetryConfirm.value = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ const q = ref('')
|
|||||||
const statusCode = ref<number | null>(null)
|
const statusCode = ref<number | null>(null)
|
||||||
const phase = ref<string>('')
|
const phase = ref<string>('')
|
||||||
const errorOwner = ref<string>('')
|
const errorOwner = ref<string>('')
|
||||||
const resolvedStatus = ref<string>('unresolved')
|
const resolvedStatus = ref<string>('unresolved')
|
||||||
|
const viewMode = ref<'errors' | 'excluded' | 'all'>('errors')
|
||||||
|
|
||||||
|
|
||||||
const modalTitle = computed(() => {
|
const modalTitle = computed(() => {
|
||||||
return props.errorType === 'upstream' ? t('admin.ops.errorDetails.upstreamErrors') : t('admin.ops.errorDetails.requestErrors')
|
return props.errorType === 'upstream' ? t('admin.ops.errorDetails.upstreamErrors') : t('admin.ops.errorDetails.requestErrors')
|
||||||
@@ -63,6 +65,14 @@ const resolvedSelectOptions = computed(() => {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const viewModeSelectOptions = computed(() => {
|
||||||
|
return [
|
||||||
|
{ value: 'errors', label: t('admin.ops.errorDetails.viewErrors') || 'errors' },
|
||||||
|
{ value: 'excluded', label: t('admin.ops.errorDetails.viewExcluded') || 'excluded' },
|
||||||
|
{ value: 'all', label: t('common.all') }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
const phaseSelectOptions = computed(() => {
|
const phaseSelectOptions = computed(() => {
|
||||||
const options = [
|
const options = [
|
||||||
{ value: '', label: t('common.all') },
|
{ value: '', label: t('common.all') },
|
||||||
@@ -88,7 +98,8 @@ async function fetchErrorLogs() {
|
|||||||
const params: Record<string, any> = {
|
const params: Record<string, any> = {
|
||||||
page: page.value,
|
page: page.value,
|
||||||
page_size: pageSize.value,
|
page_size: pageSize.value,
|
||||||
time_range: props.timeRange
|
time_range: props.timeRange,
|
||||||
|
view: viewMode.value
|
||||||
}
|
}
|
||||||
|
|
||||||
const platform = String(props.platform || '').trim()
|
const platform = String(props.platform || '').trim()
|
||||||
@@ -109,7 +120,9 @@ async function fetchErrorLogs() {
|
|||||||
else if (resolvedVal === 'unresolved') params.resolved = 'false'
|
else if (resolvedVal === 'unresolved') params.resolved = 'false'
|
||||||
// 'all' -> omit
|
// 'all' -> omit
|
||||||
|
|
||||||
const res = await opsAPI.listErrorLogs(params)
|
const res = props.errorType === 'upstream'
|
||||||
|
? await opsAPI.listUpstreamErrors(params)
|
||||||
|
: await opsAPI.listRequestErrors(params)
|
||||||
rows.value = res.items || []
|
rows.value = res.items || []
|
||||||
total.value = res.total || 0
|
total.value = res.total || 0
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -121,15 +134,17 @@ async function fetchErrorLogs() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetFilters() {
|
function resetFilters() {
|
||||||
q.value = ''
|
q.value = ''
|
||||||
statusCode.value = null
|
statusCode.value = null
|
||||||
phase.value = props.errorType === 'upstream' ? 'upstream' : ''
|
phase.value = props.errorType === 'upstream' ? 'upstream' : ''
|
||||||
errorOwner.value = ''
|
errorOwner.value = ''
|
||||||
resolvedStatus.value = 'unresolved'
|
resolvedStatus.value = 'unresolved'
|
||||||
page.value = 1
|
viewMode.value = 'errors'
|
||||||
fetchErrorLogs()
|
page.value = 1
|
||||||
}
|
fetchErrorLogs()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.show,
|
() => props.show,
|
||||||
@@ -172,7 +187,7 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => [statusCode.value, phase.value, errorOwner.value, resolvedStatus.value] as const,
|
() => [statusCode.value, phase.value, errorOwner.value, resolvedStatus.value, viewMode.value] as const,
|
||||||
() => {
|
() => {
|
||||||
if (!props.show) return
|
if (!props.show) return
|
||||||
page.value = 1
|
page.value = 1
|
||||||
@@ -186,7 +201,7 @@ watch(
|
|||||||
<div class="flex h-full min-h-0 flex-col">
|
<div class="flex h-full min-h-0 flex-col">
|
||||||
<!-- Filters -->
|
<!-- Filters -->
|
||||||
<div class="mb-4 flex-shrink-0 border-b border-gray-200 pb-4 dark:border-dark-700">
|
<div class="mb-4 flex-shrink-0 border-b border-gray-200 pb-4 dark:border-dark-700">
|
||||||
<div class="grid grid-cols-7 gap-2">
|
<div class="grid grid-cols-8 gap-2">
|
||||||
<div class="col-span-2 compact-select">
|
<div class="col-span-2 compact-select">
|
||||||
<div class="relative group">
|
<div class="relative group">
|
||||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||||
@@ -224,6 +239,10 @@ watch(
|
|||||||
<Select :model-value="resolvedStatus" :options="resolvedSelectOptions" @update:model-value="resolvedStatus = String($event ?? 'unresolved')" />
|
<Select :model-value="resolvedStatus" :options="resolvedSelectOptions" @update:model-value="resolvedStatus = String($event ?? 'unresolved')" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="compact-select">
|
||||||
|
<Select :model-value="viewMode" :options="viewModeSelectOptions" @update:model-value="viewMode = $event as any" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex items-center justify-end">
|
<div class="flex items-center justify-end">
|
||||||
<button type="button" class="rounded-lg bg-gray-100 px-3 py-1.5 text-xs font-semibold text-gray-700 transition-colors hover:bg-gray-200 dark:bg-dark-700 dark:text-gray-300 dark:hover:bg-dark-600" @click="resetFilters">
|
<button type="button" class="rounded-lg bg-gray-100 px-3 py-1.5 text-xs font-semibold text-gray-700 transition-colors hover:bg-gray-200 dark:bg-dark-700 dark:text-gray-300 dark:hover:bg-dark-600" @click="resetFilters">
|
||||||
{{ t('common.reset') }}
|
{{ t('common.reset') }}
|
||||||
|
|||||||
Reference in New Issue
Block a user