Files
vue3-element-admin/src/views/dashboard/index.vue
2024-12-14 20:32:01 +08:00

346 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="dashboard-container">
<!-- github 角标 -->
<github-corner class="github-corner" />
<el-card shadow="never">
<el-row class="h-80px">
<el-col :span="18" :xs="24">
<div class="flex-x-start">
<img
class="wh-80px rounded-full"
:src="userStore.userInfo.avatar + '?imageView2/1/w/80/h/80'"
/>
<div class="ml-5">
<p>{{ greetings }}</p>
<p class="text-sm text-gray">今日天气晴朗气温在15至25之间东南风</p>
</div>
</div>
</el-col>
<el-col :span="6" :xs="24">
<el-row class="h-80px flex-y-center" :gutter="10">
<el-col :span="10">
<div class="font-bold color-#ff9a2e text-sm flex-y-center">
<el-icon class="mr-2px"><Folder /></el-icon>
仓库
</div>
<div class="mt-3">
<el-link href="https://gitee.com/youlaiorg/vue3-element-admin" target="_blank">
<SvgIcon icon-class="gitee" class="text-lg color-#f76560" />
</el-link>
<el-divider direction="vertical" />
<el-link href="https://github.com/youlaitech/vue3-element-admin" target="_blank">
<SvgIcon icon-class="github" class="text-lg color-#4080ff" />
</el-link>
<el-divider direction="vertical" />
<el-link href="https://gitcode.com/youlai/vue3-element-admin" target="_blank">
<SvgIcon icon-class="gitcode" class="text-lg color-#ff9a2e" />
</el-link>
</div>
</el-col>
<el-col :span="10">
<div class="font-bold color-#4080ff text-sm flex-y-center">
<el-icon class="mr-2px"><Document /></el-icon>
文档
</div>
<div class="mt-3">
<el-link href="https://juejin.cn/post/7228990409909108793" target="_blank">
<SvgIcon icon-class="juejin" class="text-lg" />
</el-link>
<el-divider direction="vertical" />
<el-link
href="https://youlai.blog.csdn.net/article/details/130191394"
target="_blank"
>
<SvgIcon icon-class="csdn" class="text-lg" />
</el-link>
<el-divider direction="vertical" />
<el-link href="https://www.cnblogs.com/haoxianrui/p/17331952.html" target="_blank">
<SvgIcon icon-class="cnblogs" class="text-lg" />
</el-link>
</div>
</el-col>
<el-col :span="4">
<div class="font-bold color-#f76560 text-sm flex-y-center">
<el-icon class="mr-2px"><VideoCamera /></el-icon>
视频
</div>
<div class="mt-3">
<el-link
href="https://space.bilibili.com/1731537706/channel/seriesdetail?sid=4459672"
target="_blank"
>
<SvgIcon icon-class="bilibili" class="text-lg" />
</el-link>
</div>
</el-col>
</el-row>
</el-col>
</el-row>
</el-card>
<!-- 数据统计 -->
<el-row :gutter="10" class="mt-5">
<!-- 访客数(UV) -->
<el-col :span="12">
<el-skeleton :loading="visitStatsLoading" :rows="5" animated>
<template #template>
<el-card>
<template #header>
<div>
<el-skeleton-item variant="h3" style="width: 40%" />
<el-skeleton-item variant="rect" style="float: right; width: 1em; height: 1em" />
</div>
</template>
<div class="flex-x-between">
<el-skeleton-item variant="text" style="width: 30%" />
<el-skeleton-item variant="circle" style="width: 2em; height: 2em" />
</div>
<div class="mt-5 flex-x-between">
<el-skeleton-item variant="text" style="width: 50%" />
<el-skeleton-item variant="text" style="width: 1em" />
</div>
</el-card>
</template>
<template v-if="!visitStatsLoading">
<el-card shadow="never">
<template #header>
<div class="flex-x-between">
<span class="text-gray">访客数(UV)</span>
<el-tag type="success" size="small"></el-tag>
</div>
</template>
<div class="flex-x-between mt-2">
<div class="flex-y-center">
<span class="text-lg">{{ visitStatsData.todayUvCount }}</span>
<span
:class="['text-xs', 'ml-2', getGrowthRateClass(visitStatsData.uvGrowthRate)]"
>
<el-icon>
<Top v-if="visitStatsData.uvGrowthRate > 0" />
<Bottom v-else-if="visitStatsData.uvGrowthRate < 0" />
</el-icon>
{{ formatGrowthRate(visitStatsData.uvGrowthRate) }}
</span>
</div>
<svg-icon icon-class="visitor" size="2em" />
</div>
<div class="flex-x-between mt-2 text-sm text-gray">
<span>总访客数</span>
<span>{{ visitStatsData.totalUvCount }}</span>
</div>
</el-card>
</template>
</el-skeleton>
</el-col>
<!-- 浏览量(PV) -->
<el-col :span="12">
<el-skeleton :loading="visitStatsLoading" :rows="5" animated>
<template #template>
<el-card>
<template #header>
<div>
<el-skeleton-item variant="h3" style="width: 40%" />
<el-skeleton-item variant="rect" style="float: right; width: 1em; height: 1em" />
</div>
</template>
<div class="flex-x-between">
<el-skeleton-item variant="text" style="width: 30%" />
<el-skeleton-item variant="circle" style="width: 2em; height: 2em" />
</div>
<div class="mt-5 flex-x-between">
<el-skeleton-item variant="text" style="width: 50%" />
<el-skeleton-item variant="text" style="width: 1em" />
</div>
</el-card>
</template>
<template v-if="!visitStatsLoading">
<el-card shadow="never">
<template #header>
<div class="flex-x-between">
<span class="text-gray">浏览量(PV)</span>
<el-tag type="primary" size="small"></el-tag>
</div>
</template>
<div class="flex-x-between mt-2">
<div class="flex-y-center">
<span class="text-lg">{{ visitStatsData.todayPvCount }}</span>
<span
:class="['text-xs', 'ml-2', getGrowthRateClass(visitStatsData.pvGrowthRate)]"
>
<el-icon>
<Top v-if="visitStatsData.pvGrowthRate > 0" />
<Bottom v-else-if="visitStatsData.pvGrowthRate < 0" />
</el-icon>
{{ formatGrowthRate(visitStatsData.pvGrowthRate) }}
</span>
</div>
<svg-icon icon-class="browser" size="2em" />
</div>
<div class="flex-x-between mt-2 text-sm text-gray">
<span>总浏览量</span>
<span>{{ visitStatsData.totalPvCount }}</span>
</div>
</el-card>
</template>
</el-skeleton>
</el-col>
</el-row>
<el-row :gutter="10" class="mt-5">
<!-- 访问趋势统计图 -->
<el-col :xs="24" :span="16">
<VisitTrend id="VisitTrend" width="100%" height="400px" />
</el-col>
<!-- 通知公告 -->
<el-col :xs="24" :span="8">
<el-card>
<template #header>
<div class="flex-x-between">
<div class="flex-y-center">通知公告</div>
<el-link type="primary">
<span class="text-xs" @click="handleViewMoreNotice">查看更多</span>
<el-icon class="text-xs"><ArrowRight /></el-icon>
</el-link>
</div>
</template>
<el-scrollbar height="400px">
<div v-for="(item, index) in notices" :key="index" class="flex-y-center py-4">
<DictLabel v-model="item.type" code="notice_type" size="small" />
<el-text truncated class="!mx-2 flex-1 !text-xs !text-gray">
{{ item.title }}
</el-text>
<el-link @click="handleOpenNoticeDetail(item.id)">
<el-icon class="text-sm"><View /></el-icon>
</el-link>
</div>
</el-scrollbar>
</el-card>
</el-col>
</el-row>
<NoticeDetail ref="noticeDetailRef" />
</div>
</template>
<script setup lang="ts">
defineOptions({
name: "Dashboard",
inheritAttrs: false,
});
import VisitTrend from "./components/visit-trend.vue";
import router from "@/router";
import LogAPI, { VisitStatsVO } from "@/api/system/log";
import NoticeAPI, { NoticePageVO } from "@/api/system/notice";
import { useUserStore } from "@/store/modules/user";
import { formatGrowthRate } from "@/utils";
const noticeDetailRef = ref();
const notices = ref<NoticePageVO[]>([]);
const userStore = useUserStore();
const date: Date = new Date();
const greetings = computed(() => {
const hours = date.getHours();
if (hours >= 6 && hours < 8) {
return "晨起披衣出草堂,轩窗已自喜微凉🌅!";
} else if (hours >= 8 && hours < 12) {
return "上午好," + userStore.userInfo.nickname + "";
} else if (hours >= 12 && hours < 18) {
return "下午好," + userStore.userInfo.nickname + "";
} else if (hours >= 18 && hours < 24) {
return "晚上好," + userStore.userInfo.nickname + "";
} else {
return "偷偷向银河要了一把碎星,只等你闭上眼睛撒入你的梦中,晚安🌛!";
}
});
const visitStatsLoading = ref(true);
const visitStatsData = ref<VisitStatsVO>({
todayUvCount: 0,
uvGrowthRate: 0,
totalUvCount: 0,
todayPvCount: 0,
pvGrowthRate: 0,
totalPvCount: 0,
});
// 加载访问统计数据
const loadVisitStatsData = async () => {
LogAPI.getVisitStats()
.then((data) => {
visitStatsData.value = data;
})
.finally(() => {
visitStatsLoading.value = false;
});
};
// 根据增长率获取样式
const getGrowthRateClass = (growthRate?: number): string => {
if (!growthRate) {
return "color-[--el-color-info]";
}
if (growthRate > 0) {
return "color-[--el-color-danger]";
} else if (growthRate < 0) {
return "color-[--el-color-success]";
} else {
return "color-[--el-color-info]";
}
};
const loadMyNotice = () => {
NoticeAPI.getMyNoticePage({ pageNum: 1, pageSize: 10 }).then((data) => {
notices.value = data.list;
});
};
// 查看更多
function handleViewMoreNotice() {
router.push({ path: "/myNotice" });
}
// 打开通知公告
function handleOpenNoticeDetail(id: string) {
noticeDetailRef.value.openNotice(id);
}
onMounted(() => {
loadVisitStatsData();
loadMyNotice();
});
</script>
<style lang="scss" scoped>
.dashboard-container {
position: relative;
padding: 24px;
.github-corner {
position: absolute;
top: 0;
right: 0;
z-index: 1;
border: 0;
}
}
</style>