refactor: ♻️ 控制台统计数据动态完善

This commit is contained in:
ray
2024-07-06 15:54:28 +08:00
parent b95f940294
commit 4ef2cb1cb9

View File

@@ -1,6 +1,5 @@
<template> <template>
<div class="dashboard-container"> <div class="dashboard-container">
<!-- github角标 -->
<github-corner class="github-corner" /> <github-corner class="github-corner" />
<el-card shadow="never"> <el-card shadow="never">
@@ -55,7 +54,7 @@
<div class="flex-x-between mt-2"> <div class="flex-x-between mt-2">
<span class="text-lg"> 1</span> <span class="text-lg"> 1</span>
<svg-icon icon-class="item.iconClass" size="2em" /> <svg-icon icon-class="user" size="2em" />
</div> </div>
<div <div
class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]" class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]"
@@ -103,7 +102,9 @@
<span class="text-[var(--el-text-color-secondary)]">{{ <span class="text-[var(--el-text-color-secondary)]">{{
item.title item.title
}}</span> }}</span>
<el-tag type="primary" size="small"> </el-tag> <el-tag :type="item.tagType" size="small">
{{ item.granularity }}
</el-tag>
</div> </div>
</template> </template>
@@ -111,26 +112,25 @@
<div class="flex-y-center"> <div class="flex-y-center">
<span class="text-lg"> {{ item.todayCount }}</span> <span class="text-lg"> {{ item.todayCount }}</span>
<span <span
v-if="item.growthRate"
:class="[ :class="[
'text-xs', 'text-xs',
'ml-2', 'ml-2',
item.growthRate > 0 ? 'color-red' : 'color-green', getGrowthRateClass(item.growthRate),
]" ]"
> >
<i-ep-top v-if="item.growthRate > 0" /> <i-ep-top v-if="item.growthRate > 0" />
<i-ep-bottom v-else-if="item.growthRate < 0" /> <i-ep-bottom v-else-if="item.growthRate < 0" />
{{ Math.abs(item.growthRate * 100).toFixed(2) }}% {{ formatGrowthRate(item.growthRate) }}
</span> </span>
</div> </div>
<svg-icon :icon-class="item.type" size="2em" /> <svg-icon :icon-class="item.icon" size="2em" />
</div> </div>
<div <div
class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]" class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]"
> >
<span>{{ item.title }} </span> <span>{{ item.title }} </span>
<span> {{ item.totalCountOutput }} </span> <span> {{ item.totalCount }} </span>
</div> </div>
</el-card> </el-card>
</template> </template>
@@ -168,8 +168,6 @@ defineOptions({
inheritAttrs: false, inheritAttrs: false,
}); });
import type { EpPropMergeType } from "element-plus/es/utils/vue/props/types";
import { useUserStore } from "@/store/modules/user"; import { useUserStore } from "@/store/modules/user";
import { useTransition, TransitionPresets } from "@vueuse/core"; import { useTransition, TransitionPresets } from "@vueuse/core";
@@ -202,7 +200,7 @@ const statisticData = ref([
}, },
{ {
value: 50, value: 50,
iconClass: "todolist", iconClass: "todo",
title: "待办", title: "待办",
suffix: "/100", suffix: "/100",
key: "upcoming", key: "upcoming",
@@ -244,24 +242,80 @@ const notices = ref([
const loading = ref(true); const loading = ref(true);
const visitStatsList = ref<VisitStatsVO[] | null>(Array(3).fill({})); const visitStatsList = ref<VisitStats[] | null>(Array(3).fill({}));
interface VisitStats {
title: string;
icon: string;
tagType: "primary" | "success" | "warning";
growthRate: number;
/** 粒度 */
granularity: string;
/** 今日数量输出文档 */
todayCount: number;
totalCount: number;
}
const loadVisitStatsData = async () => { const loadVisitStatsData = async () => {
const list = await StatsAPI.getVisitStats(); const list: VisitStatsVO[] = await StatsAPI.getVisitStats();
if (list) { if (list) {
visitStatsList.value = list; const tagTypes: ("primary" | "success" | "warning")[] = [
// 初始化动画输出 "primary",
list.forEach((item) => { "success",
item.totalCountOutput = useTransition(item.totalCount, { "warning",
duration: 1000, ];
transition: TransitionPresets.easeOutExpo, const transformedList: VisitStats[] = list.map((item, index) => ({
}).value; title: item.title,
}); icon: getVisitStatsIcon(item.type),
tagType: tagTypes[index % tagTypes.length],
growthRate: item.growthRate,
granularity: "日",
todayCount: item.todayCount,
totalCount: item.totalCount,
}));
visitStatsList.value = transformedList;
loading.value = false; loading.value = false;
} }
}; };
/** 格式化增长率 */
const formatGrowthRate = (growthRate: number): string => {
if (growthRate === 0) {
return "-";
}
const formattedRate = Math.abs(growthRate * 100)
.toFixed(2)
.replace(/\.?0+$/, "");
return formattedRate + "%";
};
/** 获取增长率文本颜色类 */
const getGrowthRateClass = (growthRate: number): string => {
if (growthRate > 0) {
return "color-[--el-color-danger]";
} else if (growthRate < 0) {
return "color-[--el-color-success]";
} else {
return "color-[--el-color-info]";
}
};
/** 获取访问统计图标 */
const getVisitStatsIcon = (type: string) => {
switch (type) {
case "pv":
return "pv";
case "uv":
return "uv";
case "ip":
return "ip";
default:
return "pv";
}
};
onMounted(() => { onMounted(() => {
loadVisitStatsData(); loadVisitStatsData();
}); });