fix: 修复数据获取及展示问题,优化字段命名与UI显示

This commit is contained in:
Ray.Hao
2026-04-05 16:20:36 +08:00
parent 9654913a21
commit 00d06de84f
9 changed files with 49 additions and 52 deletions

View File

@@ -36,13 +36,10 @@ const AuthAPI = {
/** 刷新 token 接口*/
refreshToken(refreshToken: string) {
const params = new URLSearchParams();
params.append("refresh_token", refreshToken);
return request<any, LoginResponse>({
url: `${AUTH_BASE_URL}/refresh-token`,
method: "post",
data: params,
params: { refreshToken },
headers: {
Authorization: "no-auth",
},

View File

@@ -4,7 +4,7 @@ import type {
LogItem,
VisitTrendQueryParams,
VisitTrendDetail,
VisitStatsDetail,
VisitOverviewDetail,
} from "./types";
const LOG_BASE_URL = "/api/v1/logs";
@@ -30,7 +30,7 @@ const LogAPI = {
/** 获取访问概览统计 */
getVisitOverview() {
return request<any, VisitStatsDetail>({
return request<any, VisitOverviewDetail>({
url: `${LOG_BASE_URL}/analytics/overview`,
method: "get",
});

View File

@@ -68,12 +68,10 @@ export interface VisitTrendDetail {
pvList: number[];
/** 访客数(UV)列表 */
uvList: number[];
/** IP数列表 */
ipList: number[];
}
/** 访问量统计视图对象 */
export interface VisitStatsDetail {
/** 访问概览视图对象 */
export interface VisitOverviewDetail {
/** 今日独立访客数(UV) */
todayUvCount: number;
/** 累计独立访客数(UV) */

View File

@@ -132,6 +132,8 @@ function createSseConnection(options: UseSseOptions = {}) {
reader = r;
const decoder = new TextDecoder();
let buffer = "";
let currentEvent = "message";
let currentData = "";
// SSE 文本协议解析event / data / 空行分隔
const processChunk = ({
@@ -148,15 +150,13 @@ function createSseConnection(options: UseSseOptions = {}) {
const lines = buffer.split("\n");
buffer = lines.pop() || "";
let currentEvent = "message";
let currentData = "";
for (const line of lines) {
if (line.startsWith(":")) continue;
if (line.startsWith("event:")) {
currentEvent = line.slice(6).trim();
} else if (line.startsWith("data:")) {
currentData = line.slice(5).trim();
const dataLine = line.slice(5).trim();
currentData = currentData ? `${currentData}\n${dataLine}` : dataLine;
} else if (line === "") {
if (currentData) {
const handlers = eventHandlers.get(currentEvent);

View File

@@ -159,7 +159,7 @@
<!-- 访客量UV) -->
<el-col :span="8" :xs="24" class="mb-xs-3">
<el-skeleton :loading="visitStatsLoading" :rows="5" animated>
<el-skeleton :loading="visitOverviewLoading" :rows="5" animated>
<template #template>
<el-card>
<template #header>
@@ -179,7 +179,7 @@
</div>
</el-card>
</template>
<template v-if="!visitStatsLoading">
<template v-if="!visitOverviewLoading">
<el-card
shadow="never"
class="h-full transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
@@ -200,16 +200,16 @@
</span>
<span
v-if="uvGrowthText !== null"
:class="['text-xs', computeGrowthRateClass(visitStatsData.uvGrowthRate)]"
:class="['text-xs', computeGrowthRateClass(visitOverviewData.uvGrowthRate)]"
>
<el-icon
v-if="
visitStatsData.uvGrowthRate !== undefined &&
visitStatsData.uvGrowthRate !== null
visitOverviewData.uvGrowthRate !== undefined &&
visitOverviewData.uvGrowthRate !== null
"
>
<Top v-if="visitStatsData.uvGrowthRate > 0" />
<Bottom v-else-if="visitStatsData.uvGrowthRate < 0" />
<Top v-if="visitOverviewData.uvGrowthRate > 0" />
<Bottom v-else-if="visitOverviewData.uvGrowthRate < 0" />
</el-icon>
{{ uvGrowthText }}
</span>
@@ -227,7 +227,7 @@
<!-- 浏览量(PV) -->
<el-col :span="8" :xs="24">
<el-skeleton :loading="visitStatsLoading" :rows="5" animated>
<el-skeleton :loading="visitOverviewLoading" :rows="5" animated>
<template #template>
<el-card>
<template #header>
@@ -247,7 +247,7 @@
</div>
</el-card>
</template>
<template v-if="!visitStatsLoading">
<template v-if="!visitOverviewLoading">
<el-card
shadow="never"
class="h-full transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
@@ -268,16 +268,16 @@
</span>
<span
v-if="pvGrowthText !== null"
:class="['text-xs', computeGrowthRateClass(visitStatsData.pvGrowthRate)]"
:class="['text-xs', computeGrowthRateClass(visitOverviewData.pvGrowthRate)]"
>
<el-icon
v-if="
visitStatsData.pvGrowthRate !== undefined &&
visitStatsData.pvGrowthRate !== null
visitOverviewData.pvGrowthRate !== undefined &&
visitOverviewData.pvGrowthRate !== null
"
>
<Top v-if="visitStatsData.pvGrowthRate > 0" />
<Bottom v-else-if="visitStatsData.pvGrowthRate < 0" />
<Top v-if="visitOverviewData.pvGrowthRate > 0" />
<Bottom v-else-if="visitOverviewData.pvGrowthRate < 0" />
</el-icon>
{{ pvGrowthText }}
</span>
@@ -383,7 +383,7 @@ import { dayjs } from "element-plus";
import { ref } from "vue";
import { useRouter } from "vue-router";
import LogAPI from "@/api/system/log";
import type { VisitStatsDetail, VisitTrendDetail } from "@/api/system/log";
import type { VisitOverviewDetail, VisitTrendDetail } from "@/api/system/log";
import { useUserStore } from "@/store/modules/user";
import { formatGrowthRate } from "@/utils";
import { useTransition, useDateFormat } from "@vueuse/core";
@@ -434,9 +434,9 @@ const greetings = computed(() => {
});
// 访客统计数据加载状态
const visitStatsLoading = ref(true);
const visitOverviewLoading = ref(true);
// 访客统计数据
const visitStatsData = ref<VisitStatsDetail>({
const visitOverviewData = ref<VisitOverviewDetail>({
todayUvCount: 0,
uvGrowthRate: 0,
totalUvCount: 0,
@@ -447,27 +447,27 @@ const visitStatsData = ref<VisitStatsDetail>({
const uvGrowthText = computed(() => {
if (
visitStatsData.value.uvGrowthRate === undefined ||
visitStatsData.value.uvGrowthRate === null
visitOverviewData.value.uvGrowthRate === undefined ||
visitOverviewData.value.uvGrowthRate === null
) {
return "--";
}
return formatGrowthRate(visitStatsData.value.uvGrowthRate);
return formatGrowthRate(visitOverviewData.value.uvGrowthRate);
});
const pvGrowthText = computed(() => {
if (
visitStatsData.value.pvGrowthRate === undefined ||
visitStatsData.value.pvGrowthRate === null
visitOverviewData.value.pvGrowthRate === undefined ||
visitOverviewData.value.pvGrowthRate === null
) {
return "--";
}
return formatGrowthRate(visitStatsData.value.pvGrowthRate);
return formatGrowthRate(visitOverviewData.value.pvGrowthRate);
});
// 数字过渡动画
const transitionUvCount = useTransition(
computed(() => visitStatsData.value.todayUvCount),
computed(() => visitOverviewData.value.todayUvCount),
{
duration: 1000,
transition: [0.25, 0.1, 0.25, 1.0], // CSS cubic-bezier
@@ -475,7 +475,7 @@ const transitionUvCount = useTransition(
);
const transitionTotalUvCount = useTransition(
computed(() => visitStatsData.value.totalUvCount),
computed(() => visitOverviewData.value.totalUvCount),
{
duration: 1200,
transition: [0.25, 0.1, 0.25, 1.0],
@@ -483,7 +483,7 @@ const transitionTotalUvCount = useTransition(
);
const transitionPvCount = useTransition(
computed(() => visitStatsData.value.todayPvCount),
computed(() => visitOverviewData.value.todayPvCount),
{
duration: 1000,
transition: [0.25, 0.1, 0.25, 1.0],
@@ -491,7 +491,7 @@ const transitionPvCount = useTransition(
);
const transitionTotalPvCount = useTransition(
computed(() => visitStatsData.value.totalPvCount),
computed(() => visitOverviewData.value.totalPvCount),
{
duration: 1200,
transition: [0.25, 0.1, 0.25, 1.0],
@@ -520,13 +520,13 @@ const visitTrendChartOptions = ref();
/**
* 获取访客统计数据
*/
const fetchVisitStatsData = () => {
const fetchVisitOverviewData = () => {
LogAPI.getVisitOverview()
.then((data) => {
visitStatsData.value = data;
visitOverviewData.value = data;
})
.finally(() => {
visitStatsLoading.value = false;
visitOverviewLoading.value = false;
});
};
@@ -558,7 +558,7 @@ const updateVisitTrendChartOptions = (data: VisitTrendDetail) => {
trigger: "axis",
},
legend: {
data: ["浏览量(PV)", "访客UV)"],
data: ["浏览量(PV)", "访客数(UV)"],
bottom: 0,
},
grid: {
@@ -597,9 +597,9 @@ const updateVisitTrendChartOptions = (data: VisitTrendDetail) => {
},
},
{
name: "访客量UV)",
name: "访客量(UV)",
type: "line",
data: data.ipList,
data: data.uvList,
areaStyle: {
color: "rgba(103, 194, 58, 0.1)",
},
@@ -644,7 +644,7 @@ watch(
// 组件挂载后加载访客统计数据和通知公告数据
onMounted(() => {
fetchVisitStatsData();
fetchVisitOverviewData();
});
</script>

View File

@@ -245,7 +245,7 @@ function handleSelectionChange(selection: DictItem[]): void {
function openDialog(row?: DictItem): void {
resetForm();
dialogState.visible = true;
dialogState.title = row ? "编辑字典值" : "新增字典";
dialogState.title = row ? "编辑字典值" : "新增字典";
if (row?.id) {
DictAPI.getDictItemFormData(dictCode.value, row.id).then((data) => {

View File

@@ -94,7 +94,7 @@
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="50" align="center" />
<el-table-column label="用户名" prop="username" />
<el-table-column label="用户名" prop="username" min-width="120" show-overflow-tooltip />
<el-table-column label="昵称" width="200" align="center" prop="nickname" />
<el-table-column label="性别" width="100" align="center">
<template #default="scope">