feat(admin): 增加工作台大屏和转码通知

This commit is contained in:
Zhou Weidong committed 2026-10-24 17:31:16 +08:00
1 parent 2b2c6bd708
commit a266d44483
10 files changed
+860 -172

No files matched your search

+9 -103
View File
@@ -1,8 +1,9 @@
<!-- Author: fruit.com.cn -->
<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import PageHeading from '@/views/components/PageHeading.vue'
import DashboardMetricWall from '@/views/dashboard/DashboardMetricWall.vue'
import echarts from '@/plugins/echarts'
import {
getDashboardDistributionApi,
@@ -16,22 +17,7 @@ import type {
DashboardTrend,
TranscodeBoard
} from '@/api/admin/dashboard'
import { dateCell, formatCount } from '@/utils/format'
/**
* 指标展示配置:key 与后端 /admin/dashboard/overview 返回的字段一一对应,
* 不在前端编字段;口径说明见后端 DashboardService 注释与 docs/api-phase1.md「2.8」。
*/
const HIGHLIGHT_CARDS = [
{ group: 'user', key: 'total', label: '总用户数', color: '#5B8FF9' },
{ group: 'user', key: 'newToday', label: '今日新增', color: '#5AD8A6' },
{ group: 'content', key: 'dramaPublished', label: '已上线短剧', color: '#F6BD16' },
{ group: 'content', key: 'episodeWithMedia', label: '有片源剧集', color: '#E86452' },
{ group: 'playback', key: 'playTotal', label: '累计播放', color: '#6DC8EC' },
{ group: 'playback', key: 'playEvents7d', label: '近 7 天播放', color: '#945FB9' },
{ group: 'cost', key: 'transcodeRunning', label: '转码中', color: '#FF9845' },
{ group: 'cost', key: 'transcodeQueued', label: '转码排队', color: '#1E9493' }
]
import { dateCell } from '@/utils/format'
const loading = ref(false)
const overview = ref<DashboardOverview | null>(null)
@@ -62,40 +48,6 @@ const transcodeStatusLabel = {
CANCELED: '已取消'
} as const
function groupValue(groupKey: string, metricKey: string) {
const g = overview.value?.[groupKey as keyof DashboardOverview] as
| Record<string, number>
| undefined
return g?.[metricKey]
}
function display(value: unknown) {
if (value === null || value === undefined) return '暂无数据源'
if (typeof value === 'number' && !Number.isInteger(value)) return value.toFixed(3)
return String(value)
}
async function load() {
loading.value = true
try {
const [board, t, dist, queue] = await Promise.all([
getDashboardOverviewApi(),
getDashboardTrendApi(),
getDashboardDistributionApi(),
getTranscodeBoardApi()
])
overview.value = board.data || null
trend.value = t.data || null
distribution.value = dist.data || null
transcode.value = queue.data || null
lastUpdated.value = new Date().toLocaleString()
await nextTick()
renderCharts()
} finally {
loading.value = false
}
}
function renderCharts() {
disposeAll()
renderTrend()
@@ -280,26 +232,13 @@ onBeforeUnmount(() => {
<PageHeading
eyebrow="数据看板"
title="运营大盘"
description="用户、内容、播放与成本总览,以及近 7 天趋势。"
description="用户、设备、留存、内容、播放与成本总览,以及近 7 天趋势。"
>
<el-button :loading="loading" @click="loadAndRender">刷新</el-button>
</PageHeading>
<div v-loading="loading" class="board-wrap">
<!-- 顶部关键指标卡 -->
<div class="highlight-grid">
<div
v-for="card in HIGHLIGHT_CARDS"
:key="card.group + card.key"
class="highlight-card"
:style="{ borderTopColor: card.color }"
>
<div class="highlight-label">{{ card.label }}</div>
<div class="highlight-value" :class="{ empty: groupValue(card.group, card.key) === null }">
{{ display(formatCount(groupValue(card.group, card.key))) }}
</div>
</div>
</div>
<DashboardMetricWall :overview="overview" :trend="trend" />
<!-- 趋势折线图(双 Y 轴 + 柱) -->
<section class="panel chart-panel">
@@ -403,10 +342,10 @@ onBeforeUnmount(() => {
}
.panel {
background: var(--el-bg-color);
border-radius: 8px;
padding: 20px 24px;
background: var(--el-bg-color);
border: 1px solid var(--el-border-color-lighter);
border-radius: 8px;
}
.panel-heading {
@@ -434,41 +373,8 @@ onBeforeUnmount(() => {
}
.muted {
font-size: 12px;
color: var(--el-text-color-placeholder);
font-size: 12px;
}
}
.highlight-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 12px;
}
.highlight-card {
background: var(--el-bg-color);
border-radius: 8px;
padding: 14px 18px;
border: 1px solid var(--el-border-color-lighter);
border-top: 3px solid transparent;
.highlight-label {
font-size: 12px;
color: var(--el-text-color-secondary);
}
.highlight-value {
margin-top: 6px;
font-size: 24px;
font-weight: 700;
color: var(--el-text-color-primary);
font-variant-numeric: tabular-nums;
&.empty {
font-size: 14px;
color: var(--el-text-color-placeholder);
font-weight: 400;
}
}
}
@@ -514,8 +420,8 @@ onBeforeUnmount(() => {
.board-notes {
:deep(ul) {
margin: 4px 0 0;
padding-left: 18px;
margin: 4px 0 0;
font-size: 12px;
}
}