fix: 修复数据获取及展示问题,优化字段命名与UI显示
This commit is contained in:
@@ -181,8 +181,7 @@ export default defineMock([
|
|||||||
"2024-07-07",
|
"2024-07-07",
|
||||||
],
|
],
|
||||||
pvList: [1751, 5168, 4882, 5301, 4721, 4885, 1901, 1003],
|
pvList: [1751, 5168, 4882, 5301, 4721, 4885, 1901, 1003],
|
||||||
uvList: null,
|
uvList: [207, 566, 565, 631, 579, 496, 222, 152],
|
||||||
ipList: [207, 566, 565, 631, 579, 496, 222, 152],
|
|
||||||
},
|
},
|
||||||
msg: "一切ok",
|
msg: "一切ok",
|
||||||
},
|
},
|
||||||
|
|||||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -205,6 +205,9 @@ devDependencies:
|
|||||||
vite-plugin-mock-dev-server:
|
vite-plugin-mock-dev-server:
|
||||||
specifier: ^2.1.1
|
specifier: ^2.1.1
|
||||||
version: 2.1.1(vite@8.0.3)
|
version: 2.1.1(vite@8.0.3)
|
||||||
|
vue-eslint-parser:
|
||||||
|
specifier: ^10.4.0
|
||||||
|
version: 10.4.0(eslint@10.1.0)
|
||||||
vue-tsc:
|
vue-tsc:
|
||||||
specifier: ^3.2.5
|
specifier: ^3.2.5
|
||||||
version: 3.2.6(typescript@5.9.3)
|
version: 3.2.6(typescript@5.9.3)
|
||||||
|
|||||||
@@ -36,13 +36,10 @@ const AuthAPI = {
|
|||||||
|
|
||||||
/** 刷新 token 接口*/
|
/** 刷新 token 接口*/
|
||||||
refreshToken(refreshToken: string) {
|
refreshToken(refreshToken: string) {
|
||||||
const params = new URLSearchParams();
|
|
||||||
params.append("refresh_token", refreshToken);
|
|
||||||
|
|
||||||
return request<any, LoginResponse>({
|
return request<any, LoginResponse>({
|
||||||
url: `${AUTH_BASE_URL}/refresh-token`,
|
url: `${AUTH_BASE_URL}/refresh-token`,
|
||||||
method: "post",
|
method: "post",
|
||||||
data: params,
|
params: { refreshToken },
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: "no-auth",
|
Authorization: "no-auth",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type {
|
|||||||
LogItem,
|
LogItem,
|
||||||
VisitTrendQueryParams,
|
VisitTrendQueryParams,
|
||||||
VisitTrendDetail,
|
VisitTrendDetail,
|
||||||
VisitStatsDetail,
|
VisitOverviewDetail,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
|
|
||||||
const LOG_BASE_URL = "/api/v1/logs";
|
const LOG_BASE_URL = "/api/v1/logs";
|
||||||
@@ -30,7 +30,7 @@ const LogAPI = {
|
|||||||
|
|
||||||
/** 获取访问概览统计 */
|
/** 获取访问概览统计 */
|
||||||
getVisitOverview() {
|
getVisitOverview() {
|
||||||
return request<any, VisitStatsDetail>({
|
return request<any, VisitOverviewDetail>({
|
||||||
url: `${LOG_BASE_URL}/analytics/overview`,
|
url: `${LOG_BASE_URL}/analytics/overview`,
|
||||||
method: "get",
|
method: "get",
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -68,12 +68,10 @@ export interface VisitTrendDetail {
|
|||||||
pvList: number[];
|
pvList: number[];
|
||||||
/** 访客数(UV)列表 */
|
/** 访客数(UV)列表 */
|
||||||
uvList: number[];
|
uvList: number[];
|
||||||
/** IP数列表 */
|
|
||||||
ipList: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 访问量统计视图对象 */
|
/** 访问概览视图对象 */
|
||||||
export interface VisitStatsDetail {
|
export interface VisitOverviewDetail {
|
||||||
/** 今日独立访客数(UV) */
|
/** 今日独立访客数(UV) */
|
||||||
todayUvCount: number;
|
todayUvCount: number;
|
||||||
/** 累计独立访客数(UV) */
|
/** 累计独立访客数(UV) */
|
||||||
|
|||||||
@@ -132,6 +132,8 @@ function createSseConnection(options: UseSseOptions = {}) {
|
|||||||
reader = r;
|
reader = r;
|
||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
let buffer = "";
|
let buffer = "";
|
||||||
|
let currentEvent = "message";
|
||||||
|
let currentData = "";
|
||||||
|
|
||||||
// SSE 文本协议解析:event / data / 空行分隔
|
// SSE 文本协议解析:event / data / 空行分隔
|
||||||
const processChunk = ({
|
const processChunk = ({
|
||||||
@@ -148,15 +150,13 @@ function createSseConnection(options: UseSseOptions = {}) {
|
|||||||
const lines = buffer.split("\n");
|
const lines = buffer.split("\n");
|
||||||
buffer = lines.pop() || "";
|
buffer = lines.pop() || "";
|
||||||
|
|
||||||
let currentEvent = "message";
|
|
||||||
let currentData = "";
|
|
||||||
|
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
if (line.startsWith(":")) continue;
|
if (line.startsWith(":")) continue;
|
||||||
if (line.startsWith("event:")) {
|
if (line.startsWith("event:")) {
|
||||||
currentEvent = line.slice(6).trim();
|
currentEvent = line.slice(6).trim();
|
||||||
} else if (line.startsWith("data:")) {
|
} 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 === "") {
|
} else if (line === "") {
|
||||||
if (currentData) {
|
if (currentData) {
|
||||||
const handlers = eventHandlers.get(currentEvent);
|
const handlers = eventHandlers.get(currentEvent);
|
||||||
|
|||||||
@@ -159,7 +159,7 @@
|
|||||||
|
|
||||||
<!-- 访客量UV) -->
|
<!-- 访客量UV) -->
|
||||||
<el-col :span="8" :xs="24" class="mb-xs-3">
|
<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>
|
<template #template>
|
||||||
<el-card>
|
<el-card>
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -179,7 +179,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!visitStatsLoading">
|
<template v-if="!visitOverviewLoading">
|
||||||
<el-card
|
<el-card
|
||||||
shadow="never"
|
shadow="never"
|
||||||
class="h-full transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
class="h-full transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
@@ -200,16 +200,16 @@
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="uvGrowthText !== null"
|
v-if="uvGrowthText !== null"
|
||||||
:class="['text-xs', computeGrowthRateClass(visitStatsData.uvGrowthRate)]"
|
:class="['text-xs', computeGrowthRateClass(visitOverviewData.uvGrowthRate)]"
|
||||||
>
|
>
|
||||||
<el-icon
|
<el-icon
|
||||||
v-if="
|
v-if="
|
||||||
visitStatsData.uvGrowthRate !== undefined &&
|
visitOverviewData.uvGrowthRate !== undefined &&
|
||||||
visitStatsData.uvGrowthRate !== null
|
visitOverviewData.uvGrowthRate !== null
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<Top v-if="visitStatsData.uvGrowthRate > 0" />
|
<Top v-if="visitOverviewData.uvGrowthRate > 0" />
|
||||||
<Bottom v-else-if="visitStatsData.uvGrowthRate < 0" />
|
<Bottom v-else-if="visitOverviewData.uvGrowthRate < 0" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
{{ uvGrowthText }}
|
{{ uvGrowthText }}
|
||||||
</span>
|
</span>
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
|
|
||||||
<!-- 浏览量(PV) -->
|
<!-- 浏览量(PV) -->
|
||||||
<el-col :span="8" :xs="24">
|
<el-col :span="8" :xs="24">
|
||||||
<el-skeleton :loading="visitStatsLoading" :rows="5" animated>
|
<el-skeleton :loading="visitOverviewLoading" :rows="5" animated>
|
||||||
<template #template>
|
<template #template>
|
||||||
<el-card>
|
<el-card>
|
||||||
<template #header>
|
<template #header>
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!visitStatsLoading">
|
<template v-if="!visitOverviewLoading">
|
||||||
<el-card
|
<el-card
|
||||||
shadow="never"
|
shadow="never"
|
||||||
class="h-full transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
class="h-full transition-all duration-200 hover:-translate-y-0.5 hover:shadow-lg"
|
||||||
@@ -268,16 +268,16 @@
|
|||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
v-if="pvGrowthText !== null"
|
v-if="pvGrowthText !== null"
|
||||||
:class="['text-xs', computeGrowthRateClass(visitStatsData.pvGrowthRate)]"
|
:class="['text-xs', computeGrowthRateClass(visitOverviewData.pvGrowthRate)]"
|
||||||
>
|
>
|
||||||
<el-icon
|
<el-icon
|
||||||
v-if="
|
v-if="
|
||||||
visitStatsData.pvGrowthRate !== undefined &&
|
visitOverviewData.pvGrowthRate !== undefined &&
|
||||||
visitStatsData.pvGrowthRate !== null
|
visitOverviewData.pvGrowthRate !== null
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<Top v-if="visitStatsData.pvGrowthRate > 0" />
|
<Top v-if="visitOverviewData.pvGrowthRate > 0" />
|
||||||
<Bottom v-else-if="visitStatsData.pvGrowthRate < 0" />
|
<Bottom v-else-if="visitOverviewData.pvGrowthRate < 0" />
|
||||||
</el-icon>
|
</el-icon>
|
||||||
{{ pvGrowthText }}
|
{{ pvGrowthText }}
|
||||||
</span>
|
</span>
|
||||||
@@ -383,7 +383,7 @@ import { dayjs } from "element-plus";
|
|||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import LogAPI from "@/api/system/log";
|
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 { useUserStore } from "@/store/modules/user";
|
||||||
import { formatGrowthRate } from "@/utils";
|
import { formatGrowthRate } from "@/utils";
|
||||||
import { useTransition, useDateFormat } from "@vueuse/core";
|
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,
|
todayUvCount: 0,
|
||||||
uvGrowthRate: 0,
|
uvGrowthRate: 0,
|
||||||
totalUvCount: 0,
|
totalUvCount: 0,
|
||||||
@@ -447,27 +447,27 @@ const visitStatsData = ref<VisitStatsDetail>({
|
|||||||
|
|
||||||
const uvGrowthText = computed(() => {
|
const uvGrowthText = computed(() => {
|
||||||
if (
|
if (
|
||||||
visitStatsData.value.uvGrowthRate === undefined ||
|
visitOverviewData.value.uvGrowthRate === undefined ||
|
||||||
visitStatsData.value.uvGrowthRate === null
|
visitOverviewData.value.uvGrowthRate === null
|
||||||
) {
|
) {
|
||||||
return "--";
|
return "--";
|
||||||
}
|
}
|
||||||
return formatGrowthRate(visitStatsData.value.uvGrowthRate);
|
return formatGrowthRate(visitOverviewData.value.uvGrowthRate);
|
||||||
});
|
});
|
||||||
|
|
||||||
const pvGrowthText = computed(() => {
|
const pvGrowthText = computed(() => {
|
||||||
if (
|
if (
|
||||||
visitStatsData.value.pvGrowthRate === undefined ||
|
visitOverviewData.value.pvGrowthRate === undefined ||
|
||||||
visitStatsData.value.pvGrowthRate === null
|
visitOverviewData.value.pvGrowthRate === null
|
||||||
) {
|
) {
|
||||||
return "--";
|
return "--";
|
||||||
}
|
}
|
||||||
return formatGrowthRate(visitStatsData.value.pvGrowthRate);
|
return formatGrowthRate(visitOverviewData.value.pvGrowthRate);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 数字过渡动画
|
// 数字过渡动画
|
||||||
const transitionUvCount = useTransition(
|
const transitionUvCount = useTransition(
|
||||||
computed(() => visitStatsData.value.todayUvCount),
|
computed(() => visitOverviewData.value.todayUvCount),
|
||||||
{
|
{
|
||||||
duration: 1000,
|
duration: 1000,
|
||||||
transition: [0.25, 0.1, 0.25, 1.0], // CSS cubic-bezier
|
transition: [0.25, 0.1, 0.25, 1.0], // CSS cubic-bezier
|
||||||
@@ -475,7 +475,7 @@ const transitionUvCount = useTransition(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const transitionTotalUvCount = useTransition(
|
const transitionTotalUvCount = useTransition(
|
||||||
computed(() => visitStatsData.value.totalUvCount),
|
computed(() => visitOverviewData.value.totalUvCount),
|
||||||
{
|
{
|
||||||
duration: 1200,
|
duration: 1200,
|
||||||
transition: [0.25, 0.1, 0.25, 1.0],
|
transition: [0.25, 0.1, 0.25, 1.0],
|
||||||
@@ -483,7 +483,7 @@ const transitionTotalUvCount = useTransition(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const transitionPvCount = useTransition(
|
const transitionPvCount = useTransition(
|
||||||
computed(() => visitStatsData.value.todayPvCount),
|
computed(() => visitOverviewData.value.todayPvCount),
|
||||||
{
|
{
|
||||||
duration: 1000,
|
duration: 1000,
|
||||||
transition: [0.25, 0.1, 0.25, 1.0],
|
transition: [0.25, 0.1, 0.25, 1.0],
|
||||||
@@ -491,7 +491,7 @@ const transitionPvCount = useTransition(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const transitionTotalPvCount = useTransition(
|
const transitionTotalPvCount = useTransition(
|
||||||
computed(() => visitStatsData.value.totalPvCount),
|
computed(() => visitOverviewData.value.totalPvCount),
|
||||||
{
|
{
|
||||||
duration: 1200,
|
duration: 1200,
|
||||||
transition: [0.25, 0.1, 0.25, 1.0],
|
transition: [0.25, 0.1, 0.25, 1.0],
|
||||||
@@ -520,13 +520,13 @@ const visitTrendChartOptions = ref();
|
|||||||
/**
|
/**
|
||||||
* 获取访客统计数据
|
* 获取访客统计数据
|
||||||
*/
|
*/
|
||||||
const fetchVisitStatsData = () => {
|
const fetchVisitOverviewData = () => {
|
||||||
LogAPI.getVisitOverview()
|
LogAPI.getVisitOverview()
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
visitStatsData.value = data;
|
visitOverviewData.value = data;
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
visitStatsLoading.value = false;
|
visitOverviewLoading.value = false;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -558,7 +558,7 @@ const updateVisitTrendChartOptions = (data: VisitTrendDetail) => {
|
|||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: ["浏览量(PV)", "访客量UV)"],
|
data: ["浏览量(PV)", "访客数(UV)"],
|
||||||
bottom: 0,
|
bottom: 0,
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
@@ -597,9 +597,9 @@ const updateVisitTrendChartOptions = (data: VisitTrendDetail) => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "访客量UV)",
|
name: "访客量(UV)",
|
||||||
type: "line",
|
type: "line",
|
||||||
data: data.ipList,
|
data: data.uvList,
|
||||||
areaStyle: {
|
areaStyle: {
|
||||||
color: "rgba(103, 194, 58, 0.1)",
|
color: "rgba(103, 194, 58, 0.1)",
|
||||||
},
|
},
|
||||||
@@ -644,7 +644,7 @@ watch(
|
|||||||
|
|
||||||
// 组件挂载后加载访客统计数据和通知公告数据
|
// 组件挂载后加载访客统计数据和通知公告数据
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchVisitStatsData();
|
fetchVisitOverviewData();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -245,7 +245,7 @@ function handleSelectionChange(selection: DictItem[]): void {
|
|||||||
function openDialog(row?: DictItem): void {
|
function openDialog(row?: DictItem): void {
|
||||||
resetForm();
|
resetForm();
|
||||||
dialogState.visible = true;
|
dialogState.visible = true;
|
||||||
dialogState.title = row ? "编辑字典值" : "新增字典值";
|
dialogState.title = row ? "编辑字典值" : "新增字典项";
|
||||||
|
|
||||||
if (row?.id) {
|
if (row?.id) {
|
||||||
DictAPI.getDictItemFormData(dictCode.value, row.id).then((data) => {
|
DictAPI.getDictItemFormData(dictCode.value, row.id).then((data) => {
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
>
|
>
|
||||||
<el-table-column type="selection" width="50" align="center" />
|
<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="200" align="center" prop="nickname" />
|
||||||
<el-table-column label="性别" width="100" align="center">
|
<el-table-column label="性别" width="100" align="center">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
|||||||
Reference in New Issue
Block a user