perf: 调整审计台仪表盘显示内容

This commit is contained in:
“huailei000”
2022-12-01 19:02:47 +08:00
committed by huailei
parent bbc43b7ec6
commit efdbc001ff
15 changed files with 669 additions and 160 deletions

View File

@@ -0,0 +1,130 @@
<template>
<div>
<el-row :gutter="16">
<el-col :lg="12" :sm="24" class="margin-top-16">
<SummaryCountCard :config="logConfig" :items="LogItems" />
</el-col>
<el-col :lg="12" :sm="24" class="margin-top-16">
<SummaryCountCard :config="sessionConfig" :items="sessionItems" />
</el-col>
</el-row>
</div>
</template>
<script>
import SummaryCountCard from '../components/SummaryCountCard.vue'
export default {
components: { SummaryCountCard },
props: {
days: {
type: [Number, String],
default: 7
}
},
data() {
return {
logConfig: {
title: '日志数据',
tip: '日志数据'
},
sessionConfig: {
title: '会话数据',
tip: '会话数据'
},
data: {
total_count_login_users: 0,
total_count_operate_logs: 0,
total_count_change_password_logs: 0,
total_count_online_sessions: 0,
total_count_history_sessions: 0,
total_count_ftp_logs: 0
}
}
},
computed: {
LogItems() {
return [
{
title: '登录数',
body: {
route: { name: `LoginLog` },
count: this.data.total_count_login_users,
disabled: !this.$hasPerm('audits.view_userloginlog')
}
},
{
title: '操作日志数',
body: {
route: { name: `OperateLog` },
count: this.data.total_count_operate_logs,
disabled: !this.$hasPerm('audits.view_operatelog')
}
},
{
title: '改密日志数',
body: {
route: { name: `PasswordChangeLog` },
count: this.data.total_count_change_password_logs,
disabled: !this.$hasPerm('audits.view_passwordchangelog')
}
}
]
},
sessionItems() {
return [
{
title: '在线会话数',
body: {
route: { name: `SessionList`, params: { activeMenu: 'OnlineList' }},
count: this.data.total_count_online_sessions,
disabled: !this.$hasPerm('terminal.view_session')
}
},
{
title: '历史会话数',
body: {
route: { name: `SessionList`, params: { activeMenu: 'OfflineList' }},
count: this.data.total_count_history_sessions,
disabled: !this.$hasPerm('terminal.view_session')
}
},
{
title: '文件传输数',
body: {
route: { name: `FtpLog` },
count: this.data.total_count_ftp_logs,
disabled: !this.$hasPerm('audits.view_ftplog')
}
}
]
}
},
watch: {
days() {
this.getData()
}
},
mounted() {
this.getData()
},
methods: {
async getData() {
this.data = await this.$axios.get(`/api/v1/index/?days=${this.days}
&total_count_login_users=1
&total_count_operate_logs=1
&total_count_change_password_logs=1
&total_count_online_sessions=1
&total_count_history_sessions=1
&total_count_ftp_logs=1
`)
}
}
}
</script>
<style scoped>
.margin-top-16 {
margin-top: 16px;
}
</style>

View File

@@ -0,0 +1,93 @@
<template>
<el-row :gutter="16">
<el-col :lg="12" :sm="12" class="margin-top-16">
<DataCard :config="logConfig" />
</el-col>
<el-col :lg="12" :sm="12" class="margin-top-16">
<DataCard :config="assetConfig" />
</el-col>
</el-row>
</template>
<script>
import DataCard from '../components/DataCard.vue'
export default {
components: {
DataCard
},
props: {
days: {
type: [Number, String],
default: 7
}
},
data() {
const documentStyle = document.documentElement.style
const themeColor = documentStyle.getPropertyValue('--color-primary')
return {
logConfig: {
title: '登录日志',
tip: '登录日志总数',
subTitle: '登录日志总数',
icon: 'log',
color: themeColor,
chartTitle: '登录成功日志数',
data: []
},
assetConfig: {
title: '命令记录',
tip: '命令记录总数',
subTitle: '登录日志总数',
icon: 'session',
color: '#ED612B',
chartTitle: '危险命令数',
data: []
}
}
},
watch: {
days() {
this.getData()
}
},
mounted() {
this.getData()
},
methods: {
async getData() {
const data = await this.$axios.get(`/api/v1/index/?days=${this.days}
&total_count_user_login_logs=1
&total_count_user_login_success_logs=1
&total_count_commands=1
&total_count_commands_danger=1
`)
const logs = [
{ name: this.$t('dashboard.ActiveUser'), value: data.total_count_user_login_logs },
{ name: this.$t('dashboard.InActiveUser'), value: data.total_count_user_login_success_logs }
]
this.$set(this.logConfig, 'data', logs)
this.$set(this.logConfig, 'total', data.total_count_user_login_logs)
this.$set(this.logConfig, 'active', data.total_count_user_login_success_logs)
this.$set(this.logConfig, 'weekAdd', data.total_count_user_login_success_logs)
const assets = [
{ name: this.$t('dashboard.ActiveAsset'), value: data.total_count_commands },
{ name: this.$t('dashboard.InActiveAsset'), value: data.total_count_commands_danger }
]
this.$set(this.assetConfig, 'data', assets)
this.$set(this.assetConfig, 'total', data.total_count_commands)
this.$set(this.assetConfig, 'active', data.total_count_commands_danger)
this.$set(this.assetConfig, 'weekAdd', data.total_count_commands_danger)
}
}
}
</script>
<style scoped>
.left, .right {
display: inline-block;
}
.margin-top-16 {
margin-top: 16px;
}
</style>

View File

@@ -0,0 +1,117 @@
<template>
<div>
<div class="box">
<div class="head">
<Title :config="config" />
</div>
<LineChart v-bind="chartConfig" />
</div>
<SummaryCountCard :config="config" :items="summaryItems" class="margin-top-16" />
</div>
</template>
<script>
import Title from '../components/Title.vue'
import LineChart from '../components/LineChart.vue'
import SummaryCountCard from '../components/SummaryCountCard.vue'
export default {
components: {
Title,
LineChart,
SummaryCountCard
},
props: {
days: {
type: [Number, String],
default: 7
}
},
data() {
return {
config: {
title: '批量命令',
tip: '批量命令'
},
chartConfig: {
datesMetrics: [],
secondaryName: '指标名称',
secondaryData: [0]
},
data: {
total_count_online_sessions: 0,
total_count_history_sessions: 0,
total_count_ftp_logs: 0
}
}
},
computed: {
summaryItems() {
return [
{
title: '批量命令数',
body: {
route: { name: `CommandList` },
count: this.data.total_count_online_sessions,
disabled: !this.$hasPerm('terminal.view_command')
}
},
{
title: '未执行批量命令',
body: {
route: { name: `CommandList` },
count: this.data.total_count_history_sessions,
disabled: !this.$hasPerm('terminal.view_command')
}
},
{
title: '执行失败命令',
body: {
route: { name: `CommandList` },
count: this.data.total_count_ftp_logs,
disabled: !this.$hasPerm('audits.view_command')
}
}
]
}
},
watch: {
days() {
this.getData()
}
},
mounted() {
this.getData()
},
methods: {
async getData() {
const data = await this.$axios.get(`/api/v1/index/?days=${this.days}
&total_count_online_sessions=1
&total_count_history_sessions=1
&total_count_ftp_logs=1
&session_dates_metrics=1
`)
this.chartConfig.datesMetrics = data.dates_metrics_date
if (this.chartConfig.secondaryData.length > 1) {
this.chartConfig.secondaryData = data.dates_metrics_total_count_session
}
}
}
}
</script>
<style lang="scss" scoped>
.margin-top-16 {
margin-top: 16px;
}
.box {
margin-top: 16px;
padding: 20px;
background: #fff;
.head {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
}
</style>

View File

@@ -1,28 +1,43 @@
<template>
<div class="box">
<Title :config="config" style="margin-bottom: 16px;" />
<ColumnChart />
</div>
</template>
<script>
import Title from '../components/Title.vue'
import ColumnChart from '../components/ColumnChart'
export default {
components: {
ColumnChart
},
components: { Title, ColumnChart },
props: {
days: {
type: [Number, String],
default: 7
}
},
data() {
return {
config: {
title: '用户登录趋势',
tip: '用户登录趋势'
},
data: {}
}
},
created() {
watch: {
days() {
this.getData()
}
},
mounted() {
this.getData()
},
methods: {
async getData() {
this.data = await this.$axios.get(`/api/v1/index/?days=${this.days}
&total_count_login_users=1`)
}
}
}
</script>

View File

@@ -2,15 +2,17 @@
<Page>
<div v-if="this.$hasPerm('rbac.view_audit')">
<Announcement />
<SwitchDate class="switch-date" @change="onChange" />
<CardSummary :days="days" />
<el-row :gutter="16">
<el-col :lg="12" :sm="24">
审计台仪表盘
<DataSummary :days="days" />
</el-col>
<el-col :lg="12" :sm="24">
审计台仪表盘
<RightSummary :days="days" />
</el-col>
</el-row>
<TrendSummary />
<TrendSummary :days="days" />
</div>
<Page403 v-else />
</Page>
@@ -19,33 +21,53 @@
<script>
import { Announcement } from '@/components'
import { Page } from '@/layout/components'
import SwitchDate from '../components/SwitchDate'
import TrendSummary from './TrendSummary'
import DataSummary from './DataSummary'
import CardSummary from './CardSummary.vue'
import RightSummary from './RightSummary.vue'
import Page403 from '@/views/403'
export default {
components: {
Page,
Announcement,
SwitchDate,
TrendSummary,
DataSummary,
CardSummary,
RightSummary,
Page403
},
props: {
},
data() {
return {
days: 7
}
},
created() {
},
methods: {
onChange(val) {
this.days = val
}
}
}
</script>
<style scoped>
<style lang="scss" scoped>
.switch-date >>> .switch {
background: #DEE0E3!important;
.el-radio-button {
.el-radio-button__inner {
background: #DEE0E3!important;
}
}
.el-radio-button.is-active {
border-radius: 4px!important;
padding: 4px 0!important;
.el-radio-button__inner {
color: var(--color-primary)!important;
background-color: #FFF!important;
border-radius: 4px!important;
}
}
}
</style>

View File

@@ -26,6 +26,8 @@ export default {
title: this.$t('dashboard.UserData'),
tip: this.$t('dashboard.UserData'),
subTitle: this.$t('dashboard.UsersTotal'),
icon: 'users',
subIcon: 'broken-line',
color: '#FFD260',
chartTitle: this.$t('dashboard.LoginUserToday'),
data: []
@@ -34,6 +36,8 @@ export default {
title: this.$t('dashboard.AssetData'),
tip: this.$t('dashboard.AssetData'),
subTitle: this.$t('dashboard.AssetsTotal'),
icon: 'assets',
subIcon: 'broken-line',
color: themeColor,
chartTitle: this.$t('dashboard.LoginAssetToday'),
data: []

View File

@@ -0,0 +1,67 @@
<template>
<div class="box">
<div class="head">
<Title :config="config" />
</div>
<LineChart v-bind="metricsData" />
</div>
</template>
<script>
import Title from '../components/Title.vue'
import LineChart from '../components/LineChart.vue'
export default {
components: {
Title,
LineChart
},
props: {
},
data() {
return {
config: {
title: this.$t('dashboard.UserAssetActivity'),
tip: this.$t('dashboard.UserAssetActivity')
},
metricsData: {
datesMetrics: [],
primaryData: [0],
primaryName: this.$t('dashboard.LoginUsers'),
secondaryData: [0],
secondaryName: this.$t('dashboard.LoginAssets')
}
}
},
mounted() {
this.getMetricData()
},
methods: {
async getMetricData() {
const url = '/api/v1/index/?dates_metrics=1&days=7'
const data = await this.$axios.get(url)
this.metricsData.datesMetrics = data.dates_metrics_date
if (this.metricsData.primaryData.length > 1) {
this.metricsData.primaryData = data.dates_metrics_total_count_active_users
}
if (this.metricsData.secondaryData.length > 1) {
this.metricsData.secondaryData = data.dates_metrics_total_count_active_assets
}
}
}
}
</script>
<style lang="scss" scoped>
.box {
margin-top: 16px;
padding: 20px;
background: #fff;
.head {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
}
</style>

View File

@@ -5,7 +5,7 @@
<el-row :gutter="16">
<el-col :lg="12" :sm="24">
<RealTimeSummary />
<LineChart />
<UserAssetActivity />
</el-col>
<el-col :lg="12" :sm="24">
<DataSummary />
@@ -23,7 +23,7 @@ import { Announcement } from '@/components'
import { Page } from '@/layout/components'
import Page403 from '@/views/403'
import RealTimeSummary from '../components/RealTimeSummary.vue'
import LineChart from '../components/LineChart.vue'
import UserAssetActivity from './UserAssetActivity.vue'
import DataSummary from './DataSummary'
import AssetProportionSummary from './AssetProportionSummary'
import RankSummary from './RankSummary'
@@ -37,7 +37,7 @@ export default {
AssetProportionSummary,
RankSummary,
RealTimeSummary,
LineChart,
UserAssetActivity,
Page403
},
data() {
@@ -48,5 +48,4 @@ export default {
</script>
<style lang="scss" scoped>
</style>

View File

@@ -1,12 +1,5 @@
<template>
<div class="content">
<div class="head">
<span class="title">
用户/资产活跃情况
<i class="fa fa-exclamation-circle icon" />
</span>
<span class="time">更新时间2022-11-17</span>
</div>
<echarts
ref="echarts"
:options="options"

View File

@@ -9,10 +9,10 @@
<div class="add">
<span class="add-num">
{{ $tc('dashboard.WeekAdd') }}{{ config.weekAdd }}
<svg-icon icon-class="broken-line" style="font-size: 18px;" />
<svg-icon v-if="config.subIcon" :icon-class="config.subIcon" class="font" />
</span>
<span class="add-icon">
<svg-icon icon-class="users" style="font-size: 18px;" />
<svg-icon v-if="config.icon" :icon-class="config.icon" class="font" />
</span>
</div>
</div>
@@ -77,5 +77,8 @@ export default {
height: 272px!important;
}
}
.font {
font-size: 18px;
}
}
</style>

View File

@@ -1,8 +1,5 @@
<template>
<div class="box">
<div class="head">
<Title :config="config" />
</div>
<div>
<echarts
ref="echarts"
:options="options"
@@ -18,24 +15,38 @@
<script>
// eslint-disable-next-line no-unused-vars
import * as echarts from 'echarts'
import Title from './Title.vue'
import { mix } from '@/utils/theme/color'
export default {
name: 'LoginMetric',
components: { Title },
props: {
range: {
type: String,
default: 'weekly'
},
datesMetrics: {
type: Array,
default: () => []
},
primaryName: {
type: String,
default: ''
},
primaryData: {
type: Array,
default: () => []
},
secondaryName: {
type: String,
default: ''
},
secondaryData: {
type: Array,
default: () => []
}
},
data: function() {
return {
config: {
title: this.$t('dashboard.UserAssetActivity'),
tip: this.$t('dashboard.UserAssetActivity')
},
dataUrl: '',
metricsData: {
dates_metrics_date: [],
@@ -79,12 +90,7 @@ export default {
icon: 'rect',
// 图例标记的图形宽度
itemWidth: 10,
itemHeight: 10,
data: [
this.$t('dashboard.LoginCount'),
this.$t('dashboard.LoginUsers'),
this.$t('dashboard.LoginAssets')
]
itemHeight: 10
},
grid: {
left: '3%',
@@ -111,7 +117,7 @@ export default {
axisTick: {
show: false
},
data: this.metricsData.dates_metrics_date
data: this.datesMetrics
}
],
yAxis: [
@@ -145,7 +151,7 @@ export default {
animationDuration: 500,
series: [
{
name: this.$t('dashboard.LoginUsers'),
name: this.primaryName,
type: 'line',
smooth: true,
areaStyle: {
@@ -174,10 +180,10 @@ export default {
shadowBlur: 5
}
},
data: this.metricsData.dates_metrics_total_count_active_users
data: this.primaryData
},
{
name: this.$t('dashboard.LoginAssets'),
name: this.secondaryName,
type: 'line',
smooth: true,
areaStyle: {
@@ -206,7 +212,7 @@ export default {
shadowBlur: 6
}
},
data: this.metricsData.dates_metrics_total_count_active_assets
data: this.secondaryData
}
]
}
@@ -217,60 +223,15 @@ export default {
this.getMetricData()
}
},
mounted() {
this.getMetricData()
},
methods: {
async getMetricData() {
const url = '/api/v1/index/?dates_metrics=1&days=7'
const data = await this.$axios.get(url)
this.metricsData = data
const activeAssets = 'dates_metrics_total_count_active_assets'
const activeUsers = 'dates_metrics_total_count_active_users'
if (data[activeAssets].length < 1) {
this.metricsData[activeAssets] = [0]
}
if (data[activeUsers].length < 1) {
this.metricsData[activeUsers] = [0]
}
},
getDataUrl() {
this.dataUrl = this.$refs.echarts.getDataURL({
})
this.dataUrl = this.$refs.echarts.getDataURL({})
}
}
}
</script>
<style lang="scss" scoped>
.box {
margin-top: 16px;
padding: 20px;
background: #fff;
.head {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
.title {
font-style: normal;
font-weight: 500;
font-size: 16px;
line-height: 26px;
color: #1F2329;
}
.icon {
color: #BBBFC4;
cursor: pointer;
}
.time {
font-weight: 400;
font-size: 10px;
line-height: 26px;
text-align: right;
color: #8F959E;
}
}
}
.echarts {
width: 100%;
height: 266px;

View File

@@ -2,18 +2,7 @@
<div class="box">
<div class="head">
<Title :config="config" />
<span>
<el-radio-group
v-model="select"
class="switch"
size="mini"
@change="onChange"
>
<el-radio-button v-for="i in options" :key="i.value" :label="i.value">
{{ i.label }}
</el-radio-button>
</el-radio-group>
</span>
<SwitchDate @change="onChange" />
</div>
<el-table
:data="tableData"
@@ -37,10 +26,12 @@
<script>
import Title from './Title.vue'
import SwitchDate from './SwitchDate.vue'
export default {
components: {
Title
Title,
SwitchDate
},
props: {
config: {
@@ -53,24 +44,8 @@ export default {
}
},
data() {
const defaultOptions = [
{
label: this.$t('dashboard.Today'),
value: '1'
},
{
label: this.$t('dashboard.Last7Days'),
value: '7'
},
{
label: this.$t('dashboard.Last30Days'),
value: '30'
}
]
return {
select: '1',
tableData: [],
options: this.config.options || defaultOptions,
tableUrl: this.config.url + `&days=1`
}
},
@@ -83,8 +58,8 @@ export default {
this.tableData = this.config.data ? res?.[this.config.data] : res
})
},
onChange() {
this.tableUrl = this.config.url + `&days=${this.select}`
onChange(val) {
this.tableUrl = this.config.url + `&days=${val}`
this.getList()
}
}
@@ -100,30 +75,6 @@ export default {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
.switch {
background: #EFF0F1;
border-radius: 4px;
padding: 0 4px;
&>>> .el-radio-button {
.el-radio-button__inner {
border: none;
color: #8F959E;
background: #EFF0F1;
}
&.is-active {
border-radius: 4px;
padding: 4px 0;
.el-radio-button__inner {
color: var(--color-primary);
background-color: #FFF;
border-radius: 4px;
}
}
}
&>>> .el-radio-button__orig-radio:checked+.el-radio-button__inner {
box-shadow: none;
}
}
}
}
>>> .el-table th, .el-table tr {

View File

@@ -28,8 +28,8 @@ export default {
},
computed: {
options() {
const { total, active, title } = this.config
const percentage = ((active / total) * 100).toFixed(0)
const { total, active = 0, title, color } = this.config
const percentage = active === 0 ? 0 : ((active / total) * 100).toFixed(0)
return {
title: [
{
@@ -46,7 +46,7 @@ export default {
left: '48%',
top: '42%',
textAlign: 'center',
text: this.config.active,
text: active,
textStyle: {
fontSize: 24,
color: '#646A73'
@@ -61,7 +61,7 @@ export default {
legend: {
show: false
},
color: [this.config.color, 'rgba(43, 147, 124, 0.05)'],
color: [color, 'rgba(43, 147, 124, 0.05)'],
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)'

View File

@@ -0,0 +1,77 @@
<template>
<div class="box">
<div style="margin-bottom: 12px;">
<Title :config="config" />
</div>
<div class="content">
<el-row type="flex" justify="space-between">
<el-col v-for="item of items" :key="item.title" :md="8" :sm="12" :xs="12">
<SummaryCard :title="item.title" :body="item.body" />
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import Title from '../components/Title.vue'
import SummaryCard from '../components/SummaryCard'
export default {
components: { Title, SummaryCard },
props: {
config: {
type: Object,
default: () => {
return {
title: '',
tip: ''
}
}
},
items: {
type: Array,
default: () => []
}
},
data() {
return {
}
},
async mounted() {
console.log(this.items, 'i---------------------------------------------------------')
},
methods: {
}
}
</script>
<style lang="scss" scoped>
.box {
padding: 20px;
background: #FFFFFF;
.content {
.el-col {
padding-left: 16px;
border-left: 1px solid #EFF0F1;
&:first-child {
padding-left: 0;
border-left: none;
}
}
.sub {
font-style: normal;
font-weight: 400;
font-size: 12px;
line-height: 20px;
color: #646A73;
}
.num {
font-style: normal;
font-weight: 500;
font-size: 24px;
cursor: pointer;
}
}
}
</style>

View File

@@ -0,0 +1,77 @@
<template>
<span>
<el-radio-group
v-model="select"
class="switch"
size="mini"
@change="onChange"
>
<el-radio-button v-for="i in iOptions" :key="i.value" :label="i.value">
{{ i.label }}
</el-radio-button>
</el-radio-group>
</span>
</template>
<script>
export default {
props: {
options: {
type: Array,
default: () => []
}
},
data() {
const defaultOptions = [
{
label: this.$t('dashboard.Today'),
value: '1'
},
{
label: this.$t('dashboard.Last7Days'),
value: '7'
},
{
label: this.$t('dashboard.Last30Days'),
value: '30'
}
]
return {
select: '1',
iOptions: this.options.length > 0 ? this.options : defaultOptions
}
},
methods: {
onChange(val) {
this.$emit('change', val)
}
}
}
</script>
<style lang="scss" scoped>
.switch {
background: #EFF0F1;
border-radius: 4px;
padding: 0 4px;
&>>> .el-radio-button {
.el-radio-button__inner {
border: none;
color: #8F959E;
background: #EFF0F1;
}
&.is-active {
border-radius: 4px;
padding: 4px 0;
.el-radio-button__inner {
color: var(--color-primary);
background-color: #FFF;
border-radius: 4px;
}
}
}
&>>> .el-radio-button__orig-radio:checked+.el-radio-button__inner {
box-shadow: none;
}
}
</style>