add 日志模块

This commit is contained in:
soga_wyj
2025-12-16 15:17:00 +08:00
parent 5676a8425f
commit e75b964e5a
8 changed files with 392 additions and 3 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,4 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*

View File

@@ -0,0 +1,19 @@
import { get, post } from '@/api/request';
const env = import.meta.env;
const PREFIX = env.VITE_API_PREFIX_SYSTEM;
// 列表
export const pageApi = (params: any) => {
return post(`${PREFIX}systemLog/page`, params);
};
// 获取某天每小时在线用户统计
export const getHourlyOnlineStatisticsApi = (params: any) => {
return get(`${PREFIX}systemLog/getHourlyOnlineStatistics`, params);
};
// 统计某时间段(天)的用户登录数
export const getUserLoginStatisticsApi = (params: any) => {
return get(`${PREFIX}systemLog/getUserLoginStatistics`, params);
};

View File

@@ -103,7 +103,7 @@ align-items: center;
left: 0;
right: 0;
bottom: 0;
z-index: 9999;
z-index: 999;
height: 100vh;
width: 100vm;
}

View File

@@ -311,6 +311,12 @@ export default [
name: 'systemApplication',
component: () => import('@/views/system/application/index.vue'),
},
{
title: '日志管理',
path: '/system/logs',
name: 'systemLogs',
component: () => import('@/views/system/logs/index.vue'),
},
{
title: '动态表格',
path: '/system/dynamicTable',

View File

@@ -76,6 +76,7 @@ const lang = {
: 'Configuration Management',
: 'User Group Management',
: 'Application Management',
: 'Log Management',
: 'Dynamic Table',
: 'Work Load',
},

View File

@@ -76,6 +76,7 @@ const lang = {
: '配置管理',
: '用户组管理',
: '应用管理',
: '日志管理',
: '动态表格',
: '工作负载',
},

View File

@@ -0,0 +1,305 @@
<template>
<div class="container">
<div class="content">
<div class="chart-box">
<EchartCard
title="在线用户统计"
ref="onlineUserChartRef"
:charts-id="'chart-1'"
:bar-type="'barChart'"
@refresh="changeOnlineUserFun"
>
<template #formSlot>
<el-form :model="onlineUserFromData" :inline="true">
<el-form-item label="日期">
<el-date-picker
v-model="date"
type="date"
placeholder="选择日期"
:size="size"
:clearable="false"
:disabled-date="disabledDate"
:teleported="true"
@change="changeDateFun"
/>
</el-form-item>
</el-form>
</template>
</EchartCard>
</div>
<div class="chart-box">
<EchartCard
title="用户登录数分析"
ref="userLoginChartRef"
:charts-id="'chart-2'"
:bar-type="'lineChart'"
@refresh="changeUserLoginFun"
>
<template #formSlot>
<el-form :model="userLoginFromData" :inline="true">
<el-form-item label="日期">
<el-date-picker
v-model="userLoginFromData.dateRange"
type="daterange"
range-separator=" ~ "
start-placeholder="开始日期"
end-placeholder="结束日期"
:clearable="false"
:size="size"
value-format="YYYY-MM-DD"
:disabled-date="disabledDate"
@change="changeDateRangeFun"
/>
</el-form-item>
</el-form>
</template>
</EchartCard>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, onMounted } from 'vue';
import { getHourlyOnlineStatisticsApi, getUserLoginStatisticsApi } from '@/api/system/systemLog';
import EchartCard from '@/components/common/echartCard/index.vue';
import dayjs from 'dayjs';
import { getThemeColor } from '@/utils/theme';
// ********************** 筛选条件部分 **********************
const size = ref<'default' | 'large' | 'small'>('default');
const date = ref(dayjs(new Date()).format('YYYY-MM-DD'));
const onlineUserFromData = ref<any>({
date: date.value,
});
const disabledDate = (time: Date) => {
return time.getTime() > Date.now();
};
const changeDateFun = (val: any) => {
onlineUserFromData.value.date = dayjs(new Date(val)).format('YYYY-MM-DD');
changeOnlineUserFun();
};
const userLoginFromData = ref<any>({
// dateRange默认设置近7天
dateRange: [ dayjs(new Date()).subtract(6, 'day').format('YYYY-MM-DD'), dayjs(new Date()).format('YYYY-MM-DD') ],
});
const changeDateRangeFun = () => {
changeUserLoginFun();
};
onMounted(() => {
changeOnlineUserFun();
changeUserLoginFun();
});
// ********************** 每小时在线用户统计 **********************
const onlineUserChartRef = ref();
const changeOnlineUserFun = () => {
onlineUserStatistics();
};
const onlineUserStatistics = async () => {
const { data } = await getHourlyOnlineStatisticsApi({ ...onlineUserFromData.value });
const xData: any = [];
const yData: any = [];
data.hourlyStats.forEach((item: any) => {
xData.push(item.hour + '点');
yData.push(item.onlineCount);
});
const options = {
title: {
show: false,
},
tooltip: {
trigger: 'axis',
},
legend: {
show: false,
},
grid: {
top: '5%',
},
xAxis: {
type: 'category',
data: xData,
},
yAxis: {
type: 'value',
minInterval: 1,
},
dataZoom:
xData.length > 10
? [
{
type: 'slider',
show: true,
xAxisIndex: [0],
start: 0,
end: 100,
textStyle: {
color: 'transparent',
},
maxValueSpan: 10,
minValueSpan: 10,
moveHandleSize: 8,
height: 0,
filterMode: 'empty',
bottom: 6,
},
]
: null,
series: [
{
name: '在线用户数',
type: 'bar',
barWidth: '30%',
data: yData,
itemStyle: {
color: getThemeColor('--el-color-success'),
},
},
],
};
onlineUserChartRef.value.commonChartRef.disposeEchartsByKey('chart-1');
onlineUserChartRef.value.commonChartRef.option = { ...options };
onlineUserChartRef.value.commonChartRef.initChart();
};
// ********************** 用户登录数分析 **********************
const userLoginChartRef = ref();
const changeUserLoginFun = () => {
userLogintStatistics();
};
const userLogintStatistics = async () => {
const createTimeArr =
dayjs(new Date(userLoginFromData.value.dateRange[0])).format('YYYY-MM-DD 00:00:00') +
',' +
dayjs(new Date(userLoginFromData.value.dateRange[1])).format('YYYY-MM-DD 23:59:59');
const params = { createTimeArr };
const { data } = await getUserLoginStatisticsApi(params);
const xData: any = [];
const yData: any = [];
data.forEach((item: any) => {
xData.push(item.date);
yData.push(item.loginUserCount);
});
// const xData = ['2025-12-10', '2025-12-11', '2025-12-12', '2025-12-13', '2025-12-14', '2025-12-15', '2025-12-16', '2025-12-17'];
// const yData = [120, 200, 150, 80, 70, 110, 130, 100];
const optionsLine = {
tooltip: {
trigger: 'axis',
},
grid: {
top: '5%',
},
xAxis: {
type: 'category',
boundaryGap: 30,
data: xData,
},
yAxis: {
type: 'value',
minInterval: 1,
},
series: [
{
name: '登录用户数',
type: 'line',
stack: 'Total',
data: yData,
lineStyle: {
width: 2,
color: getThemeColor('--el-color-primary'),
},
itemStyle: {
normal: {
color: getThemeColor('--el-color-primary'),
},
},
},
],
dataZoom:
xData.length > 5
? [
{
type: 'slider',
show: true,
xAxisIndex: [0],
start: 0,
end: 100,
textStyle: {
color: 'transparent',
},
maxValueSpan: 5,
minValueSpan: 5,
moveHandleSize: 8,
height: 0,
filterMode: 'empty',
bottom: 6,
},
]
: null,
legend: {
show: false,
},
};
userLoginChartRef.value.commonChartRef.disposeEchartsByKey('chart-2');
userLoginChartRef.value.commonChartRef.option = { ...optionsLine };
userLoginChartRef.value.commonChartRef.initChart();
};
</script>
<style lang="scss" scoped>
.container {
.content {
height: 100%;
display: flex;
flex-wrap: wrap;
gap: 12px;
overflow: auto;
.margin-style {
width: 100% !important;
}
.chart-box {
width: calc(50% - 6px);
min-width: 400px;
height: 380px;
position: relative;
.el-form {
width: 100%;
display: flex;
justify-content: flex-end;
.el-form-item {
margin-bottom: 0 !important;
margin-right: 0 !important;
.el-select {
width: 215px;
}
.select-width {
width: 90px !important;
}
.margin-right-12 {
margin-right: 12px;
}
}
}
.chart-item {
width: 100%;
height: 100%;
}
}
}
::v-deep .el-form-item__content {
width: 240px;
}
}
</style>

View File

@@ -0,0 +1,58 @@
<template>
<div class="gl-page-content-grey-full">
<div class="table">
<BaseTable
ref="baseTableRef"
tableName="SYSTEM_LOGS"
:api="pageApi"
:actionList="actionList"
fullHeight
:searchAttrs="{
createTimeArr: {
'disabled-date': disabledDateFun,
},
}"
>
<template #logType="{ row }">
<el-tag type="success" v-if="row.logType === '0' ">正常</el-tag>
<el-tag type="danger" v-else>异常</el-tag>
</template>
</BaseTable>
</div>
<div class="charts">
<LineChart></LineChart>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import BaseTable from '@/components/common/table/baseTable.vue';
import LineChart from './components/lineChart.vue';
import { pageApi } from '@/api/system/systemLog';
const baseTableRef = ref<any>();
const actionList = ref([]);
const disabledDateFun = (time: Date) => {
return time.getTime() > Date.now();
};
</script>
<style lang="scss" scoped>
.gl-page-content-grey-full {
.table {
width: 100%;
height: 50%;
min-height: 300px;
background-color: #fff;
padding: 10px;
}
.charts {
width: 100%;
margin-top: 12px;
}
}
</style>