| <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> |