mirror of
https://github.com/ReCloudStudio/WebHooker.git
synced 2026-09-22 16:11:29 +00:00
feat(admin): add detailed send log viewer with extended fields
This commit is contained in:
parent
3a6a2cbbc5
commit
dcbe93be91
14 changed files with 460 additions and 42 deletions
|
|
@ -11,11 +11,18 @@
|
|||
<p v-else-if="!loading && !logs.length" class="empty-log">{{ t("logs.empty") }}</p>
|
||||
|
||||
<div class="log-list">
|
||||
<article v-for="l in logs" :key="l.ts + l.routeId" class="log-entry" :class="{ fail: !l.ok }">
|
||||
<article
|
||||
v-for="l in logs"
|
||||
:key="l.id ?? l.ts + l.routeId"
|
||||
class="log-entry"
|
||||
:class="{ fail: !l.ok, clickable: true }"
|
||||
@click="openDetail(l)"
|
||||
>
|
||||
<div class="log-head">
|
||||
<span class="dot" :class="l.ok ? 'ok' : 'bad'"></span>
|
||||
<span class="log-route">{{ l.routeId }}</span>
|
||||
<span class="log-event">{{ l.event }}</span>
|
||||
<span v-if="l.status" class="log-status" :class="l.ok ? 'ok' : 'bad'">{{ l.status }}</span>
|
||||
<span class="log-time">{{ fmtTime(l.ts) }}</span>
|
||||
</div>
|
||||
<div class="log-meta">
|
||||
|
|
@ -31,6 +38,30 @@
|
|||
<div v-if="!l.ok && l.error" class="log-error">{{ l.error }}</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<div v-if="detailOpen" class="modal-overlay" @click.self="closeDetail">
|
||||
<div class="log-detail">
|
||||
<div class="detail-head">
|
||||
<h3>{{ t("logs.detailTitle") }}</h3>
|
||||
<button class="icon-btn" @click="closeDetail">✕</button>
|
||||
</div>
|
||||
|
||||
<p v-if="detailLoading" class="empty-log">{{ t("status.loading") }}</p>
|
||||
<p v-else-if="detailError" class="err">{{ detailError }}</p>
|
||||
|
||||
<dl v-else-if="detail" class="detail-grid">
|
||||
<template v-for="row in detailRows" :key="row.label">
|
||||
<dt>{{ row.label }}</dt>
|
||||
<dd>
|
||||
<code v-if="row.code">{{ row.value }}</code>
|
||||
<span v-else>{{ row.value }}</span>
|
||||
</dd>
|
||||
</template>
|
||||
</dl>
|
||||
|
||||
<p v-else class="err">{{ t("logs.detailMissing") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -38,10 +69,48 @@
|
|||
import type { SendRecord } from "~/types";
|
||||
|
||||
const { t } = useI18n();
|
||||
const { loadById } = useSendLogs();
|
||||
|
||||
const props = defineProps<{ logs: SendRecord[]; loading: boolean }>();
|
||||
const emit = defineEmits<{ (e: "refresh"): void }>();
|
||||
|
||||
const detailOpen = ref(false);
|
||||
const detail = ref<SendRecord | null>(null);
|
||||
const detailLoading = ref(false);
|
||||
const detailError = ref("");
|
||||
|
||||
const detailRows = computed(() => {
|
||||
const l = detail.value;
|
||||
if (!l) return [] as Array<{ label: string; value: string; code?: boolean }>;
|
||||
const rows: Array<{ label: string; value: string; code?: boolean }> = [
|
||||
{ label: t("logs.id"), value: String(l.id ?? "-"), code: true },
|
||||
{ label: t("logs.time"), value: fmtTime(l.ts) },
|
||||
{ label: t("logs.route"), value: l.routeId, code: true },
|
||||
{ label: t("logs.event"), value: l.event, code: true },
|
||||
{ label: t("logs.action"), value: l.action || "-", code: true },
|
||||
{ label: t("logs.actor"), value: l.actor || "-", code: true },
|
||||
{ label: t("logs.deliveryId"), value: l.deliveryId || "-", code: true },
|
||||
{ label: t("logs.repo"), value: l.repo || "-", code: true },
|
||||
{ label: t("logs.target"), value: l.target, code: true },
|
||||
{ label: t("logs.platform"), value: l.platform || "-", code: true },
|
||||
{ label: t("logs.status"), value: l.status != null ? String(l.status) : "-", code: true },
|
||||
{ label: t("logs.messageId"), value: l.messageId || "-", code: true },
|
||||
{ label: t("logs.errorCode"), value: l.errorCode || "-", code: true },
|
||||
{ label: t("logs.duration"), value: l.durationMs != null ? `${l.durationMs} ms` : "-" },
|
||||
{ label: t("logs.attempts"), value: l.attempts != null ? String(l.attempts) : "-" },
|
||||
{ label: t("logs.ok"), value: l.ok ? t("logs.yes") : t("logs.no") },
|
||||
{ label: t("logs.error"), value: l.error || "-" },
|
||||
];
|
||||
if (l.detail && Object.keys(l.detail).length > 0) {
|
||||
rows.push({
|
||||
label: t("logs.detail"),
|
||||
value: JSON.stringify(l.detail, null, 2),
|
||||
code: true,
|
||||
});
|
||||
}
|
||||
return rows;
|
||||
});
|
||||
|
||||
function fmtTime(ts: number): string {
|
||||
return new Date(ts).toLocaleString();
|
||||
}
|
||||
|
|
@ -49,4 +118,24 @@ function fmtTime(ts: number): string {
|
|||
function refresh(): void {
|
||||
emit("refresh");
|
||||
}
|
||||
|
||||
async function openDetail(l: SendRecord): Promise<void> {
|
||||
if (l.id == null) return;
|
||||
detailOpen.value = true;
|
||||
detail.value = null;
|
||||
detailError.value = "";
|
||||
detailLoading.value = true;
|
||||
try {
|
||||
detail.value = await loadById(l.id);
|
||||
} catch (err) {
|
||||
detailError.value = err instanceof Error ? err.message : String(err);
|
||||
} finally {
|
||||
detailLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function closeDetail(): void {
|
||||
detailOpen.value = false;
|
||||
detail.value = null;
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue