|
|
@@ -9,7 +9,9 @@
|
|
|
<el-icon :size="40"><User /></el-icon>
|
|
|
</div>
|
|
|
<div class="stat-info">
|
|
|
- <div class="stat-value">{{ dashboardData?.users?.total || 0 }}</div>
|
|
|
+ <div class="stat-value">
|
|
|
+ {{ dashboardData?.users?.total || 0 }}
|
|
|
+ </div>
|
|
|
<div class="stat-label">用户总数</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -28,14 +30,22 @@
|
|
|
<el-icon :size="40"><VideoCamera /></el-icon>
|
|
|
</div>
|
|
|
<div class="stat-info">
|
|
|
- <div class="stat-value">{{ dashboardData?.cameras?.total || 0 }}</div>
|
|
|
+ <div class="stat-value">
|
|
|
+ {{ dashboardData?.cameras?.total || 0 }}
|
|
|
+ </div>
|
|
|
<div class="stat-label">摄像头总数</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="stat-footer">
|
|
|
- <span class="online">在线: {{ dashboardData?.cameras?.online_count || 0 }}</span>
|
|
|
- <span class="offline">离线: {{ dashboardData?.cameras?.offline_count || 0 }}</span>
|
|
|
- <span class="error">异常: {{ dashboardData?.cameras?.error_count || 0 }}</span>
|
|
|
+ <span class="online"
|
|
|
+ >在线: {{ dashboardData?.cameras?.online_count || 0 }}</span
|
|
|
+ >
|
|
|
+ <span class="offline"
|
|
|
+ >离线: {{ dashboardData?.cameras?.offline_count || 0 }}</span
|
|
|
+ >
|
|
|
+ <span class="error"
|
|
|
+ >异常: {{ dashboardData?.cameras?.error_count || 0 }}</span
|
|
|
+ >
|
|
|
</div>
|
|
|
</el-card>
|
|
|
</el-col>
|
|
|
@@ -47,13 +57,20 @@
|
|
|
<el-icon :size="40"><Film /></el-icon>
|
|
|
</div>
|
|
|
<div class="stat-info">
|
|
|
- <div class="stat-value">{{ dashboardData?.videos?.total || 0 }}</div>
|
|
|
+ <div class="stat-value">
|
|
|
+ {{ dashboardData?.videos?.total || 0 }}
|
|
|
+ </div>
|
|
|
<div class="stat-label">视频总数</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="stat-footer">
|
|
|
<span>总播放: {{ dashboardData?.videos?.total_views || 0 }}</span>
|
|
|
- <span>总时长: {{ formatDuration(dashboardData?.videos?.total_duration || 0) }}</span>
|
|
|
+ <span
|
|
|
+ >总时长:
|
|
|
+ {{
|
|
|
+ formatDuration(dashboardData?.videos?.total_duration || 0)
|
|
|
+ }}</span
|
|
|
+ >
|
|
|
</div>
|
|
|
</el-card>
|
|
|
</el-col>
|
|
|
@@ -65,13 +82,20 @@
|
|
|
<el-icon :size="40"><VideoCameraFilled /></el-icon>
|
|
|
</div>
|
|
|
<div class="stat-info">
|
|
|
- <div class="stat-value">{{ dashboardData?.live_sessions?.live_count || 0 }}</div>
|
|
|
+ <div class="stat-value">
|
|
|
+ {{ dashboardData?.live_sessions?.live_count || 0 }}
|
|
|
+ </div>
|
|
|
<div class="stat-label">正在直播</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
<div class="stat-footer">
|
|
|
- <span>会话总数: {{ dashboardData?.live_sessions?.total || 0 }}</span>
|
|
|
- <span>峰值观众: {{ dashboardData?.live_sessions?.max_peak_viewers || 0 }}</span>
|
|
|
+ <span
|
|
|
+ >会话总数: {{ dashboardData?.live_sessions?.total || 0 }}</span
|
|
|
+ >
|
|
|
+ <span
|
|
|
+ >峰值观众:
|
|
|
+ {{ dashboardData?.live_sessions?.max_peak_viewers || 0 }}</span
|
|
|
+ >
|
|
|
</div>
|
|
|
</el-card>
|
|
|
</el-col>
|
|
|
@@ -84,26 +108,41 @@
|
|
|
<template #header>
|
|
|
<div class="card-header">
|
|
|
<span>今日统计</span>
|
|
|
- <el-button type="primary" link :icon="Refresh" @click="loadDashboardData">刷新</el-button>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ link
|
|
|
+ :icon="Refresh"
|
|
|
+ @click="loadDashboardData"
|
|
|
+ >刷新</el-button
|
|
|
+ >
|
|
|
</div>
|
|
|
</template>
|
|
|
<el-row :gutter="40">
|
|
|
<el-col :xs="24" :sm="8">
|
|
|
- <el-statistic title="今日观看次数" :value="dashboardData?.today?.today_views || 0">
|
|
|
+ <el-statistic
|
|
|
+ title="今日观看次数"
|
|
|
+ :value="dashboardData?.today?.today_views || 0"
|
|
|
+ >
|
|
|
<template #prefix>
|
|
|
<el-icon><View /></el-icon>
|
|
|
</template>
|
|
|
</el-statistic>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="8">
|
|
|
- <el-statistic title="今日新增用户" :value="dashboardData?.today?.today_new_users || 0">
|
|
|
+ <el-statistic
|
|
|
+ title="今日新增用户"
|
|
|
+ :value="dashboardData?.today?.today_new_users || 0"
|
|
|
+ >
|
|
|
<template #prefix>
|
|
|
<el-icon><UserFilled /></el-icon>
|
|
|
</template>
|
|
|
</el-statistic>
|
|
|
</el-col>
|
|
|
<el-col :xs="24" :sm="8">
|
|
|
- <el-statistic title="今日直播场次" :value="dashboardData?.today?.today_sessions || 0">
|
|
|
+ <el-statistic
|
|
|
+ title="今日直播场次"
|
|
|
+ :value="dashboardData?.today?.today_sessions || 0"
|
|
|
+ >
|
|
|
<template #prefix>
|
|
|
<el-icon><Videocamera /></el-icon>
|
|
|
</template>
|
|
|
@@ -125,12 +164,22 @@
|
|
|
</template>
|
|
|
<el-descriptions :column="3" border>
|
|
|
<el-descriptions-item label="数据库状态">
|
|
|
- <el-tag :type="dashboardData?.system?.database === 'healthy' ? 'success' : 'danger'">
|
|
|
- {{ dashboardData?.system?.database === 'healthy' ? '正常' : '异常' }}
|
|
|
+ <el-tag
|
|
|
+ :type="
|
|
|
+ dashboardData?.system?.database === 'healthy'
|
|
|
+ ? 'success'
|
|
|
+ : 'danger'
|
|
|
+ "
|
|
|
+ >
|
|
|
+ {{
|
|
|
+ dashboardData?.system?.database === "healthy"
|
|
|
+ ? "正常"
|
|
|
+ : "异常"
|
|
|
+ }}
|
|
|
</el-tag>
|
|
|
</el-descriptions-item>
|
|
|
<el-descriptions-item label="API 版本">
|
|
|
- {{ dashboardData?.system?.api_version || '-' }}
|
|
|
+ {{ dashboardData?.system?.api_version || "-" }}
|
|
|
</el-descriptions-item>
|
|
|
<el-descriptions-item label="数据更新时间">
|
|
|
{{ lastUpdateTime }}
|
|
|
@@ -143,8 +192,8 @@
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
-import { ref, onMounted, computed } from 'vue'
|
|
|
-import { ElMessage } from 'element-plus'
|
|
|
+import { ref, onMounted, computed } from "vue";
|
|
|
+import { ElMessage } from "element-plus";
|
|
|
import {
|
|
|
User,
|
|
|
VideoCamera,
|
|
|
@@ -153,50 +202,49 @@ import {
|
|
|
Refresh,
|
|
|
View,
|
|
|
UserFilled,
|
|
|
- Videocamera
|
|
|
-} from '@element-plus/icons-vue'
|
|
|
-import { getDashboardStats, type DashboardData } from '@/api/stats'
|
|
|
+} from "@element-plus/icons-vue";
|
|
|
+import { getDashboardStats, type DashboardData } from "@/api/stats";
|
|
|
|
|
|
-const loading = ref(false)
|
|
|
-const dashboardData = ref<DashboardData | null>(null)
|
|
|
-const lastUpdate = ref<Date | null>(null)
|
|
|
+const loading = ref(false);
|
|
|
+const dashboardData = ref<DashboardData | null>(null);
|
|
|
+const lastUpdate = ref<Date | null>(null);
|
|
|
|
|
|
const lastUpdateTime = computed(() => {
|
|
|
- if (!lastUpdate.value) return '-'
|
|
|
- return lastUpdate.value.toLocaleString('zh-CN')
|
|
|
-})
|
|
|
+ if (!lastUpdate.value) return "-";
|
|
|
+ return lastUpdate.value.toLocaleString("zh-CN");
|
|
|
+});
|
|
|
|
|
|
function formatDuration(seconds: number): string {
|
|
|
- if (!seconds) return '0秒'
|
|
|
- const hours = Math.floor(seconds / 3600)
|
|
|
- const minutes = Math.floor((seconds % 3600) / 60)
|
|
|
+ if (!seconds) return "0秒";
|
|
|
+ const hours = Math.floor(seconds / 3600);
|
|
|
+ const minutes = Math.floor((seconds % 3600) / 60);
|
|
|
if (hours > 0) {
|
|
|
- return `${hours}小时${minutes}分钟`
|
|
|
+ return `${hours}小时${minutes}分钟`;
|
|
|
}
|
|
|
- return `${minutes}分钟`
|
|
|
+ return `${minutes}分钟`;
|
|
|
}
|
|
|
|
|
|
async function loadDashboardData() {
|
|
|
- loading.value = true
|
|
|
+ loading.value = true;
|
|
|
try {
|
|
|
- const res = await getDashboardStats()
|
|
|
+ const res = await getDashboardStats();
|
|
|
if (res.code === 200) {
|
|
|
- dashboardData.value = res.data
|
|
|
- lastUpdate.value = new Date()
|
|
|
+ dashboardData.value = res.data;
|
|
|
+ lastUpdate.value = new Date();
|
|
|
} else {
|
|
|
- ElMessage.error(res.msg || '获取统计数据失败')
|
|
|
+ ElMessage.error(res.msg || "获取统计数据失败");
|
|
|
}
|
|
|
} catch (error) {
|
|
|
- console.error('Failed to load dashboard data:', error)
|
|
|
- ElMessage.error('获取统计数据失败')
|
|
|
+ console.error("Failed to load dashboard data:", error);
|
|
|
+ ElMessage.error("获取统计数据失败");
|
|
|
} finally {
|
|
|
- loading.value = false
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
- loadDashboardData()
|
|
|
-})
|
|
|
+ loadDashboardData();
|
|
|
+});
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
@@ -255,9 +303,15 @@ onMounted(() => {
|
|
|
font-size: 12px;
|
|
|
color: #909399;
|
|
|
|
|
|
- .online { color: #67c23a; }
|
|
|
- .offline { color: #909399; }
|
|
|
- .error { color: #f56c6c; }
|
|
|
+ .online {
|
|
|
+ color: #67c23a;
|
|
|
+ }
|
|
|
+ .offline {
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ .error {
|
|
|
+ color: #f56c6c;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
&.users .stat-icon {
|