refactor(report): remove duplicated detail section and keep compact cards

This commit is contained in:
2026-02-07 10:16:35 +08:00
parent 462e12ca0d
commit 2d5be0feb2
24 changed files with 62 additions and 434 deletions

View File

@@ -38,7 +38,6 @@ const dockerStats = ref(null)
const browserPoolStats = ref(null)
const systemConfig = ref(null)
const queueTab = ref('running')
const detailPanels = ref([])
function recordUpdatedAt() {
try {
@@ -460,344 +459,6 @@ onUnmounted(() => {
</div>
</el-card>
</section>
<el-collapse v-model="detailPanels" class="detail-collapse">
<el-collapse-item name="detail">
<template #title>
<span class="detail-collapse-title">查看详细数据队列明细 / 线程池明细 / 系统资源明细</span>
</template>
<div class="desktop-report">
<el-row :gutter="12">
<el-col :xs="24" :lg="12">
<el-card shadow="never" class="panel" :body-style="{ padding: '16px' }">
<div class="panel-head">
<div class="head-left">
<div class="head-icon tone-purple">
<el-icon><TrendCharts /></el-icon>
</div>
<div class="head-text">
<div class="panel-title">任务概览</div>
<div class="panel-sub app-muted">
<template v-if="normalizeCount(taskToday.total_tasks) > 0">
今日成功率 {{ taskTodaySuccessRate }}% · {{ runningCountsLabel }}
</template>
<template v-else>今日无任务 · {{ runningCountsLabel }}</template>
</div>
</div>
</div>
<el-progress
type="circle"
:percentage="normalizeCount(taskToday.total_tasks) > 0 ? Math.round(taskTodaySuccessRate) : 0"
:width="74"
:stroke-width="10"
:status="normalizeCount(taskToday.total_tasks) === 0 ? 'success' : taskTodaySuccessRate >= 90 ? 'success' : taskTodaySuccessRate >= 60 ? 'warning' : 'exception'"
/>
</div>
<div class="metrics-block">
<div class="block-title">今日</div>
<MetricGrid :items="taskTodayCards" :loading="loading" :min-width="120" />
</div>
<div class="divider"></div>
<div class="metrics-block">
<div class="block-title">累计</div>
<MetricGrid :items="taskTotalCards" :loading="loading" :min-width="120" />
</div>
</el-card>
</el-col>
<el-col :xs="24" :lg="12">
<el-card shadow="never" class="panel" :body-style="{ padding: '16px' }">
<div class="panel-head">
<div class="head-left">
<div class="head-icon tone-blue">
<el-icon><Tickets /></el-icon>
</div>
<div class="head-text">
<div class="panel-title">队列监控</div>
<div class="panel-sub app-muted">{{ runningCountsLabel }}</div>
</div>
</div>
</div>
<el-tabs v-model="queueTab" class="queue-tabs" stretch>
<el-tab-pane name="running">
<template #label>
<span class="tab-label">
运行中
<el-tag size="small" effect="light" type="success">{{ runningCount }}</el-tag>
</span>
</template>
<div class="table-wrap">
<el-table :data="runningTaskList.slice(0, 10)" size="small" style="width: 100%">
<el-table-column label="用户" min-width="120">
<template #default="{ row }">{{ row.user_username || '-' }}</template>
</el-table-column>
<el-table-column label="账号" min-width="150">
<template #default="{ row }">{{ row.username || '-' }}</template>
</el-table-column>
<el-table-column label="来源" width="100">
<template #default="{ row }">{{ sourceLabel(row.source) }}</template>
</el-table-column>
<el-table-column label="类型" width="90">
<template #default="{ row }">{{ row.browse_type || '-' }}</template>
</el-table-column>
<el-table-column label="进度" width="100">
<template #default="{ row }">{{ row.progress_items }}/{{ row.progress_attachments }}</template>
</el-table-column>
<el-table-column label="耗时" width="100">
<template #default="{ row }">{{ row.elapsed_display || '-' }}</template>
</el-table-column>
<el-table-column label="状态" min-width="140">
<template #default="{ row }">{{ row.detail_status || row.status || '-' }}</template>
</el-table-column>
</el-table>
</div>
<div v-if="runningCount === 0" class="help app-muted">当前无运行任务</div>
</el-tab-pane>
<el-tab-pane name="queuing">
<template #label>
<span class="tab-label">
排队中
<el-tag size="small" effect="light" type="warning">{{ queuingCount }}</el-tag>
</span>
</template>
<div class="table-wrap">
<el-table :data="queuingTaskList.slice(0, 10)" size="small" style="width: 100%">
<el-table-column label="用户" min-width="120">
<template #default="{ row }">{{ row.user_username || '-' }}</template>
</el-table-column>
<el-table-column label="账号" min-width="150">
<template #default="{ row }">{{ row.username || '-' }}</template>
</el-table-column>
<el-table-column label="来源" width="100">
<template #default="{ row }">{{ sourceLabel(row.source) }}</template>
</el-table-column>
<el-table-column label="类型" width="90">
<template #default="{ row }">{{ row.browse_type || '-' }}</template>
</el-table-column>
<el-table-column label="等待" width="100">
<template #default="{ row }">{{ row.elapsed_display || '-' }}</template>
</el-table-column>
<el-table-column label="状态" min-width="160">
<template #default="{ row }">{{ row.detail_status || row.status || '-' }}</template>
</el-table-column>
</el-table>
</div>
<div v-if="queuingCount === 0" class="help app-muted">当前无排队任务</div>
</el-tab-pane>
</el-tabs>
</el-card>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :xs="24" :lg="12">
<el-card shadow="never" class="panel" :body-style="{ padding: '16px' }">
<div class="panel-head">
<div class="head-left">
<div class="head-icon tone-cyan">
<el-icon><Message /></el-icon>
</div>
<div class="head-text">
<div class="panel-title">邮件报表</div>
<div class="panel-sub app-muted">成功率 {{ emailSuccessRate }}%</div>
</div>
</div>
</div>
<MetricGrid :items="emailCards" :loading="loading" :min-width="132" />
<div class="divider"></div>
<div class="metrics-block">
<div class="block-title">类型统计</div>
<MetricGrid :items="emailTypeCards" :loading="loading" :min-width="132" />
</div>
</el-card>
</el-col>
<el-col :xs="24" :lg="12">
<el-card shadow="never" class="panel" :body-style="{ padding: '16px' }">
<div class="panel-head">
<div class="head-left">
<div class="head-icon tone-orange">
<el-icon><ChatLineSquare /></el-icon>
</div>
<div class="head-text">
<div class="panel-title">反馈概览</div>
<div class="panel-sub app-muted">待处理 {{ normalizeCount(feedbackStats?.pending) }} </div>
</div>
</div>
</div>
<MetricGrid :items="feedbackCards" :loading="loading" :min-width="145" />
<div class="help app-muted">提示反馈处理越及时用户留存与满意度越高</div>
</el-card>
</el-col>
</el-row>
<el-row :gutter="12">
<el-col :xs="24" :lg="12">
<el-card shadow="never" class="panel" :body-style="{ padding: '16px' }">
<div class="panel-head">
<div class="head-left">
<div class="head-icon tone-green">
<el-icon><Cpu /></el-icon>
</div>
<div class="head-text">
<div class="panel-title">系统资源</div>
<div class="panel-sub app-muted">服务器与容器运行状态</div>
</div>
</div>
<el-tag v-if="serverInfo?.uptime" effect="light" type="info">运行 {{ serverInfo.uptime }}</el-tag>
</div>
<div class="resource-grid">
<div class="resource-item">
<div class="resource-k app-muted">CPU</div>
<el-progress
:percentage="Math.round(parsePercent(serverInfo?.cpu_percent))"
:status="parsePercent(serverInfo?.cpu_percent) >= 90 ? 'exception' : parsePercent(serverInfo?.cpu_percent) >= 75 ? 'warning' : 'success'"
/>
<div class="resource-sub app-muted">{{ Math.round(parsePercent(serverInfo?.cpu_percent)) }}%</div>
</div>
<div class="resource-item">
<div class="resource-k app-muted">内存</div>
<el-progress
:percentage="Math.round(parsePercent(serverInfo?.memory_percent))"
:status="parsePercent(serverInfo?.memory_percent) >= 90 ? 'exception' : parsePercent(serverInfo?.memory_percent) >= 75 ? 'warning' : 'success'"
/>
<div class="resource-sub app-muted">
{{ serverInfo?.memory_used || '-' }} / {{ serverInfo?.memory_total || '-' }}{{ Math.round(parsePercent(serverInfo?.memory_percent)) }}%
</div>
</div>
<div class="resource-item">
<div class="resource-k app-muted">磁盘</div>
<el-progress
:percentage="Math.round(parsePercent(serverInfo?.disk_percent))"
:status="parsePercent(serverInfo?.disk_percent) >= 90 ? 'exception' : parsePercent(serverInfo?.disk_percent) >= 75 ? 'warning' : 'success'"
/>
<div class="resource-sub app-muted">
{{ serverInfo?.disk_used || '-' }} / {{ serverInfo?.disk_total || '-' }}{{ Math.round(parsePercent(serverInfo?.disk_percent)) }}%
</div>
</div>
</div>
<div class="divider"></div>
<div class="block-title">容器</div>
<el-descriptions border :column="2" size="small">
<el-descriptions-item label="状态">{{ dockerStats?.status || '-' }}</el-descriptions-item>
<el-descriptions-item label="容器名">{{ dockerStats?.container_name || '-' }}</el-descriptions-item>
<el-descriptions-item label="运行时长">{{ dockerStats?.uptime || '-' }}</el-descriptions-item>
<el-descriptions-item label="CPU">{{ dockerStats?.cpu_percent || '-' }}</el-descriptions-item>
<el-descriptions-item label="内存">{{ dockerStats?.memory_usage || '-' }}</el-descriptions-item>
<el-descriptions-item label="内存占比">{{ dockerStats?.memory_percent || '-' }}</el-descriptions-item>
</el-descriptions>
<div class="divider"></div>
<div class="panel-head">
<div class="head-left">
<div class="head-text">
<div class="panel-title">截图线程池</div>
<div class="panel-sub app-muted">
活跃有执行环境{{ browserPoolActiveWorkers }} · 忙碌 {{ browserPoolBusyWorkers }} · 队列 {{ browserPoolQueueSize }}
</div>
</div>
</div>
<el-tag v-if="browserPoolStats?.server_time_cst" effect="light" type="info">{{ browserPoolStats.server_time_cst }}</el-tag>
</div>
<MetricGrid :items="browserPoolCards" :loading="loading" :min-width="120" />
<div class="divider"></div>
<div class="table-wrap">
<el-table :data="browserPoolWorkers" size="small" border>
<el-table-column prop="worker_id" label="Worker" width="90" />
<el-table-column label="状态" width="90">
<template #default="{ row }">
<el-tag :type="workerPoolStatusType(row)" effect="light">{{ workerPoolStatusLabel(row) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="执行" width="90">
<template #default="{ row }">
<el-tag :type="workerRunTagType(row)" effect="light">{{ workerRunLabel(row) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="任务" width="120">
<template #default="{ row }">
<span>{{ normalizeCount(row?.total_tasks) }}</span>
<span class="app-muted"> / </span>
<span :class="normalizeCount(row?.failed_tasks) ? 'err' : 'app-muted'">{{ normalizeCount(row?.failed_tasks) }}</span>
</template>
</el-table-column>
<el-table-column prop="browser_use_count" label="复用" width="90" />
<el-table-column prop="last_active_at" label="最近活跃" min-width="160" />
<el-table-column prop="browser_created_at" label="环境创建" min-width="160" />
</el-table>
</div>
</el-card>
</el-col>
<el-col :xs="24" :lg="12">
<el-card shadow="never" class="panel" :body-style="{ padding: '16px' }">
<div class="panel-head">
<div class="head-left">
<div class="head-icon tone-red">
<el-icon><Tools /></el-icon>
</div>
<div class="head-text">
<div class="panel-title">配置概览</div>
<div class="panel-sub app-muted">定时 / 代理 / 并发</div>
</div>
</div>
</div>
<div class="config-grid">
<div class="config-item">
<div class="config-k app-muted">定时任务</div>
<div class="config-v">
<el-tag v-if="scheduleEnabled" type="success" effect="light">启用</el-tag>
<el-tag v-else type="info" effect="light">关闭</el-tag>
<span class="config-inline app-muted"> {{ scheduleTime }} / {{ scheduleBrowseType }}</span>
</div>
<div class="config-sub app-muted">日期{{ scheduleWeekdaysDisplay || scheduleWeekdays || '-' }}</div>
</div>
<div class="config-item">
<div class="config-k app-muted">代理</div>
<div class="config-v">
<el-tag v-if="proxyEnabled" type="success" effect="light">启用</el-tag>
<el-tag v-else type="info" effect="light">关闭</el-tag>
<span v-if="proxyEnabled && proxyApiUrl" class="config-inline app-muted">{{ proxyApiUrl }}</span>
</div>
<div class="config-sub app-muted">有效期{{ proxyExpireMinutes || '-' }} 分钟</div>
</div>
<div class="config-item">
<div class="config-k app-muted">并发</div>
<div class="config-v">
<span>全局 {{ maxConcurrentGlobal || '-' }}</span>
<span class="config-split app-muted">/</span>
<span>单账号 {{ maxConcurrentPerAccount || '-' }}</span>
<span class="config-split app-muted">/</span>
<span>截图 {{ maxScreenshotConcurrent || '-' }}</span>
</div>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
@@ -960,35 +621,6 @@ onUnmounted(() => {
display: none;
}
.detail-collapse {
border: 1px solid rgba(17, 24, 39, 0.08);
border-radius: 14px;
background: rgba(255, 255, 255, 0.72);
overflow: hidden;
}
.detail-collapse :deep(.el-collapse-item__header) {
padding: 0 14px;
min-height: 42px;
font-size: 13px;
font-weight: 800;
background: rgba(248, 250, 252, 0.92);
border-bottom: 1px solid rgba(17, 24, 39, 0.08);
}
.detail-collapse :deep(.el-collapse-item__wrap) {
border: none;
background: transparent;
}
.detail-collapse :deep(.el-collapse-item__content) {
padding: 14px;
}
.detail-collapse-title {
color: #0f172a;
}
.panel {
border-radius: 18px;
border: 1px solid rgba(17, 24, 39, 0.1);
@@ -1200,10 +832,6 @@ onUnmounted(() => {
gap: 10px;
}
.detail-collapse {
display: none;
}
.report-hero {
border-radius: 14px;
padding: 12px;