|
<template> |
|
<div class="debug-view p-6"> |
|
<div class="mb-4"> |
|
<t-button theme="primary" @click="loadDebugData" :loading="loading"> |
|
<template #icon> |
|
<t-icon name="refresh" /> |
|
</template> |
|
刷新数据 |
|
</t-button> |
|
</div> |
|
|
|
<t-loading :loading="loading" text="正在加载调试数据..."> |
|
<div v-if="error" class="mb-4"> |
|
<t-alert theme="error" :message="error" /> |
|
</div> |
|
|
|
<div v-if="!loading && debugList.length === 0" class="text-center py-12"> |
|
<t-icon name="inbox" size="48px" class="text-gray-400 mb-4" /> |
|
<p class="text-gray-500">暂无调试数据</p> |
|
</div> |
|
|
|
<div v-else class="space-y-4"> |
|
<t-card |
|
v-for="item in debugList" |
|
:key="item.debugId" |
|
:class="{ 'border-red-200': item.hasError }" |
|
class="cursor-pointer hover:shadow-md transition-shadow" |
|
@click="toggleDebugContent(item.debugId)" |
|
> |
|
<template #header> |
|
<div class="flex justify-between items-center"> |
|
<div class="flex items-center space-x-3"> |
|
<strong class="text-lg">{{ item.email }}</strong> |
|
<t-tag |
|
:theme="item.hasError ? 'danger' : 'success'" |
|
variant="light" |
|
> |
|
{{ item.hasError ? '失败' : '成功' }} |
|
</t-tag> |
|
<span class="text-gray-500 text-sm"> |
|
{{ formatTime(item.timestamp) }} |
|
</span> |
|
</div> |
|
<t-icon |
|
:name="expandedItems.has(item.debugId) ? 'chevron-up' : 'chevron-down'" |
|
class="text-gray-400" |
|
/> |
|
</div> |
|
</template> |
|
|
|
<div v-if="expandedItems.has(item.debugId)" class="space-y-4"> |
|
<div v-if="item.hasError" class="mb-4"> |
|
<t-alert theme="error" :message="`错误信息: ${item.errorMessage}`" /> |
|
</div> |
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm"> |
|
<div> |
|
<span class="font-medium text-gray-700">调试ID:</span> |
|
<span class="ml-2 text-gray-600 font-mono">{{ item.debugId }}</span> |
|
</div> |
|
<div> |
|
<span class="font-medium text-gray-700">邮箱:</span> |
|
<span class="ml-2 text-gray-600">{{ item.email }}</span> |
|
</div> |
|
<div> |
|
<span class="font-medium text-gray-700">时间:</span> |
|
<span class="ml-2 text-gray-600">{{ formatTime(item.timestamp) }}</span> |
|
</div> |
|
<div> |
|
<span class="font-medium text-gray-700">页面标题:</span> |
|
<span class="ml-2 text-gray-600">{{ item.title }}</span> |
|
</div> |
|
<div class="md:col-span-2"> |
|
<span class="font-medium text-gray-700">页面URL:</span> |
|
<span class="ml-2 text-gray-600 break-all">{{ item.url }}</span> |
|
</div> |
|
</div> |
|
|
|
<div> |
|
<h4 class="font-medium text-gray-700 mb-2">页面截图:</h4> |
|
<div class="border rounded-lg overflow-hidden bg-gray-50"> |
|
<img |
|
:src="item.screenshotUrl" |
|
:alt="`${item.email} 的页面截图`" |
|
class="w-full h-auto max-h-96 object-contain" |
|
loading="lazy" |
|
@error="handleImageError" |
|
/> |
|
</div> |
|
</div> |
|
</div> |
|
</t-card> |
|
</div> |
|
</t-loading> |
|
</div> |
|
</template> |
|
|
|
<script setup lang="ts"> |
|
import { ref, onMounted } from 'vue' |
|
|
|
interface DebugItem { |
|
debugId: string |
|
email: string |
|
timestamp: string |
|
url: string |
|
title: string |
|
hasError: boolean |
|
errorMessage?: string |
|
screenshotUrl: string |
|
} |
|
|
|
const loading = ref(false) |
|
const error = ref('') |
|
const debugList = ref<DebugItem[]>([]) |
|
const expandedItems = ref(new Set<string>()) |
|
|
|
const loadDebugData = async () => { |
|
loading.value = true |
|
error.value = '' |
|
|
|
try { |
|
const response = await fetch('/api/debug/list') |
|
const result = await response.json() |
|
|
|
if (result.success) { |
|
debugList.value = result.data |
|
} else { |
|
error.value = `加载失败: ${result.error}` |
|
} |
|
} catch (err) { |
|
error.value = `网络错误: ${err instanceof Error ? err.message : String(err)}` |
|
} finally { |
|
loading.value = false |
|
} |
|
} |
|
|
|
const toggleDebugContent = (debugId: string) => { |
|
if (expandedItems.value.has(debugId)) { |
|
expandedItems.value.delete(debugId) |
|
} else { |
|
expandedItems.value.add(debugId) |
|
} |
|
} |
|
|
|
const formatTime = (timestamp: string) => { |
|
return new Date(timestamp).toLocaleString('zh-CN') |
|
} |
|
|
|
const handleImageError = (event: Event) => { |
|
const img = event.target as HTMLImageElement |
|
img.style.display = 'none' |
|
const parent = img.parentElement |
|
if (parent) { |
|
parent.innerHTML = '<div class="text-center py-8 text-gray-500">截图加载失败</div>' |
|
} |
|
} |
|
|
|
onMounted(() => { |
|
loadDebugData() |
|
}) |
|
</script> |