refactor: ♻️ 访问统计对接后台临时提交

This commit is contained in:
haoxianrui
2024-07-06 05:07:50 +08:00
parent 7345b4dd66
commit d852698499
6 changed files with 176 additions and 185 deletions

View File

@@ -15,6 +15,33 @@ class LogAPI {
params: queryParams, params: queryParams,
}); });
} }
/**
* 获取访问趋势
*
* @param queryParams
* @returns
*/
static getVisitTrend(queryParams: VisitTrendQuery) {
return request<any, VisitTrendVO>({
url: `${LOG_BASE_URL}/visit-trend`,
method: "get",
params: queryParams,
});
}
/**
* 获取访问趋势
*
* @param queryParams
* @returns
*/
static getVisitStats() {
return request<any, VisitStatsVO[]>({
url: `${LOG_BASE_URL}/visit-stats`,
method: "get",
});
}
} }
export default LogAPI; export default LogAPI;
@@ -54,3 +81,40 @@ export interface LogPageVO {
/** 操作人 */ /** 操作人 */
operator: string; operator: string;
} }
/** 访问趋势视图对象 */
export interface VisitTrendVO {
/** 日期列表 */
dates: string[];
/** 浏览量(PV) */
pvList: number[];
/** 访客数(UV) */
uvList: number[];
/** IP数 */
ipList: number[];
}
/** 访问趋势查询参数 */
export interface VisitTrendQuery {
/** 开始日期 */
startDate: string;
/** 结束日期 */
endDate: string;
}
/** 访问统计 */
export interface VisitStatsVO {
/** 标题 */
title: string;
/** 类型 */
type: "pv" | "uv" | "ip";
/** 今日访问量 */
todayCount: number;
/** 总访问量 */
totalCount: number;
/** 同比增长率(相对于昨天同一时间段的增长率) */
growthRate: number;
totalCountOutput: number;
}

View File

@@ -1,35 +0,0 @@
import request from "@/utils/request";
const STATS_BASE_URL = "/api/v1/stats";
class StatsAPI {
static getVisitTrend(queryParams: VisitTrendQuery) {
return request<any, VisitTrendVO>({
url: `${STATS_BASE_URL}/visit-trend`,
method: "get",
params: queryParams,
});
}
}
export default StatsAPI;
/** 访问趋势视图对象 */
export interface VisitTrendVO {
/** 日期列表 */
dates: string[];
/** 浏览量(PV) */
pvList: number[];
/** 访客数(UV) */
uvList: number[];
/** IP数 */
ipList: number[];
}
/** 访问趋势查询参数 */
export interface VisitTrendQuery {
/** 开始日期 */
startDate: string;
/** 结束日期 */
endDate: string;
}

View File

Before

Width:  |  Height:  |  Size: 890 B

After

Width:  |  Height:  |  Size: 890 B

View File

@@ -19,6 +19,12 @@
<!-- 语言选择 --> <!-- 语言选择 -->
<lang-select class="setting-item" /> <lang-select class="setting-item" />
<el-dropdown class="setting-item" trigger="click">
<el-badge is-dot class="mt-[16px]">
<i-ep-bell />
</el-badge>
</el-dropdown>
</template> </template>
<!-- 用户头像 --> <!-- 用户头像 -->

View File

@@ -30,7 +30,7 @@
<script setup lang="ts"> <script setup lang="ts">
import * as echarts from "echarts"; import * as echarts from "echarts";
import StatsAPI, { VisitTrendVO, VisitTrendQuery } from "@/api/stats"; import StatsAPI, { VisitTrendVO, VisitTrendQuery } from "@/api/log";
const dataRange = ref(1); const dataRange = ref(1);
const chart: Ref<echarts.ECharts | null> = ref(null); const chart: Ref<echarts.ECharts | null> = ref(null);

View File

@@ -42,51 +42,93 @@
<!-- 数据卡片 --> <!-- 数据卡片 -->
<el-row :gutter="10" class="mt-5"> <el-row :gutter="10" class="mt-5">
<el-col :xs="24" :sm="12" :lg="6">
<el-card shadow="never">
<template #header>
<div class="flex-x-between">
<span class="text-[var(--el-text-color-secondary)]"
>在线用户</span
>
<el-tag type="success" size="small">-</el-tag>
</div>
</template>
<div class="flex-x-between mt-2">
<span class="text-lg"> 1</span>
<svg-icon icon-class="item.iconClass" size="2em" />
</div>
<div
class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]"
>
<span> 总用户数 </span>
<span>5 </span>
</div>
</el-card>
</el-col>
<el-col <el-col
:xs="24" :xs="24"
:sm="12" :sm="12"
:lg="6" :lg="6"
v-for="(item, index) in cardData" v-for="(item, index) in visitStatsList"
:key="index" :key="index"
> >
<el-card shadow="never"> <el-skeleton :loading="loading" animated>
<template #header> <template #template>
<div class="flex items-center justify-between"> <div>
<span class="text-[var(--el-text-color-secondary)]">{{ <el-skeleton-item variant="text" style="width: 60%" />
item.title <div class="mt-2">
}}</span> <el-skeleton-item variant="text" style="width: 40%" />
<el-tag v-if="item.tagText" :type="item.tagType" size="small"> <el-skeleton-item
{{ item.tagText }} variant="rect"
</el-tag> style="float: right; width: 2em; height: 2em"
/>
</div>
<div class="mt-2">
<el-skeleton-item variant="text" style="width: 100%" />
<el-skeleton-item variant="text" style="width: 80%" />
</div>
</div> </div>
</template> </template>
<template v-if="!loading">
<el-card shadow="never">
<template #header>
<div class="flex-x-between">
<span class="text-[var(--el-text-color-secondary)]">{{
item.title
}}</span>
<el-tag type="primary" size="small"> </el-tag>
</div>
</template>
<div class="flex items-center justify-between mt-2"> <div class="flex-x-between mt-2">
<div class="flex-y-center"> <div class="flex-y-center">
<span class="text-lg"> {{ Math.round(item.count) }}</span> <span class="text-lg"> {{ item.todayCount }}</span>
<span <span
v-if="item.growthRate" v-if="item.growthRate"
:class="[ :class="[
'text-xs', 'text-xs',
'ml-2', 'ml-2',
item.growthRate > 0 ? 'color-red' : 'color-green', item.growthRate > 0 ? 'color-red' : 'color-green',
]" ]"
><i-ep-top v-if="item.growthRate > 0" /><i-ep-bottom >
v-else-if="item.growthRate < 0" <i-ep-top v-if="item.growthRate > 0" />
/> <i-ep-bottom v-else-if="item.growthRate < 0" />
{{ Math.abs(item.growthRate * 100) }}% {{ Math.abs(item.growthRate * 100).toFixed(2) }}%
</span> </span>
</div> </div>
<svg-icon :icon-class="item.iconClass" size="2em" /> <svg-icon :icon-class="item.type" size="2em" />
</div> </div>
<div <div
class="flex items-center justify-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.dataDesc }} </span> <span>{{ item.title }} </span>
<span> {{ item.totalCount }} </span> <span> {{ item.totalCountOutput }} </span>
</div> </div>
</el-card> </el-card>
</template>
</el-skeleton>
</el-col> </el-col>
</el-row> </el-row>
@@ -115,18 +157,20 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import type { EpPropMergeType } from "element-plus/es/utils/vue/props/types";
defineOptions({ defineOptions({
name: "Dashboard", name: "Dashboard",
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";
import StatsAPI, { VisitStatsVO } from "@/api/log";
const userStore = useUserStore(); const userStore = useUserStore();
const date: Date = new Date();
const date: Date = new Date();
const greetings = computed(() => { const greetings = computed(() => {
const hours = date.getHours(); const hours = date.getHours();
if (hours >= 6 && hours < 8) { if (hours >= 6 && hours < 8) {
@@ -142,40 +186,6 @@ const greetings = computed(() => {
} }
}); });
const duration = 5000;
// 在线用户数
const onlineUserCount = ref(0);
const onlineUserCountOutput = useTransition(onlineUserCount, {
duration: duration,
transition: TransitionPresets.easeOutExpo,
});
onlineUserCount.value = 1;
// 浏览量
const pvCount = ref(0);
const pvCountOutput = useTransition(pvCount, {
duration: duration,
transition: TransitionPresets.easeOutExpo,
});
pvCount.value = 2000;
// 访客数
const uvCount = ref(0);
const uvCountOutput = useTransition(uvCount, {
duration: duration,
transition: TransitionPresets.easeOutExpo,
});
uvCount.value = 2000;
// IP数
const ipCount = ref(0);
const ipCountOutput = useTransition(ipCount, {
duration: duration,
transition: TransitionPresets.easeOutExpo,
});
ipCount.value = 2000;
// 右上角数量 // 右上角数量
const statisticData = ref([ const statisticData = ref([
{ {
@@ -199,63 +209,6 @@ const statisticData = ref([
}, },
]); ]);
interface CardProp {
title: string;
tagType: EpPropMergeType<
StringConstructor,
"primary" | "success" | "info" | "warning" | "danger",
unknown
>;
tagText: string;
count: any;
totalCount: any;
dataDesc: string;
iconClass: string;
growthRate?: number;
}
// 卡片数量
const cardData = ref<CardProp[]>([
{
title: "在线用户",
tagType: "success",
tagText: "-",
count: onlineUserCountOutput,
totalCount: "3",
dataDesc: "总用户数",
iconClass: "visit",
},
{
title: "浏览量(PV)",
tagType: "primary",
tagText: "日",
count: pvCountOutput,
totalCount: 3000,
dataDesc: "总浏览量",
iconClass: "pv",
growthRate: 0.5,
},
{
title: "访客数(UV)",
tagType: "danger",
tagText: "日",
count: uvCountOutput,
totalCount: 3000,
dataDesc: "总访客数",
iconClass: "uv",
growthRate: -0.1,
},
{
title: "IP数",
tagType: "success",
tagText: "日",
count: ipCountOutput,
totalCount: 3000,
dataDesc: "总IP数",
iconClass: "ip",
growthRate: 0.2,
},
]);
const notices = ref([ const notices = ref([
{ {
title: "v2.12.0", title: "v2.12.0",
@@ -282,6 +235,30 @@ const notices = ref([
description: "修复了一些问题,优化了一些代码。", description: "修复了一些问题,优化了一些代码。",
}, },
]); ]);
const loading = ref(true);
const visitStatsList = ref<VisitStatsVO[] | null>(Array(3).fill({}));
const loadVisitStatsData = async () => {
const list = await StatsAPI.getVisitStats();
if (list) {
visitStatsList.value = list;
// 初始化动画输出
list.forEach((item) => {
item.totalCountOutput = useTransition(item.totalCount, {
duration: 1000,
transition: TransitionPresets.easeOutExpo,
}).value;
});
loading.value = false;
}
};
onMounted(() => {
loadVisitStatsData();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@@ -289,12 +266,6 @@ const notices = ref([
position: relative; position: relative;
padding: 24px; padding: 24px;
.user-avatar {
width: 40px;
height: 40px;
border-radius: 50%;
}
.github-corner { .github-corner {
position: absolute; position: absolute;
top: 0; top: 0;
@@ -302,20 +273,5 @@ const notices = ref([
z-index: 1; z-index: 1;
border: 0; border: 0;
} }
.data-box {
display: flex;
justify-content: space-between;
padding: 20px;
font-weight: bold;
color: var(--el-text-color-regular);
background: var(--el-bg-color-overlay);
border-color: var(--el-border-color);
box-shadow: var(--el-box-shadow-dark);
}
.svg-icon {
fill: currentcolor !important;
}
} }
</style> </style>