add 日志模块
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,5 +1,4 @@
|
||||
# Logs
|
||||
logs
|
||||
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
||||
19
src/api/system/systemLog.ts
Normal file
19
src/api/system/systemLog.ts
Normal 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);
|
||||
};
|
||||
|
||||
@@ -103,7 +103,7 @@ align-items: center;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 9999;
|
||||
z-index: 999;
|
||||
height: 100vh;
|
||||
width: 100vm;
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -76,6 +76,7 @@ const lang = {
|
||||
配置管理: 'Configuration Management',
|
||||
用户组管理: 'User Group Management',
|
||||
应用管理: 'Application Management',
|
||||
日志管理: 'Log Management',
|
||||
动态表格: 'Dynamic Table',
|
||||
工作负载: 'Work Load',
|
||||
},
|
||||
|
||||
@@ -76,6 +76,7 @@ const lang = {
|
||||
配置管理: '配置管理',
|
||||
用户组管理: '用户组管理',
|
||||
应用管理: '应用管理',
|
||||
日志管理: '日志管理',
|
||||
动态表格: '动态表格',
|
||||
工作负载: '工作负载',
|
||||
},
|
||||
|
||||
305
src/views/system/logs/components/lineChart.vue
Normal file
305
src/views/system/logs/components/lineChart.vue
Normal 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>
|
||||
58
src/views/system/logs/index.vue
Normal file
58
src/views/system/logs/index.vue
Normal 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>
|
||||
Reference in New Issue
Block a user