mirror of https://github.com/halo-dev/halo-admin
parent
7a8f97c701
commit
d5eace6a05
|
@ -205,6 +205,13 @@ export const asyncRouterMap = [
|
|||
component: () => import('@/views/system/ToolList'),
|
||||
meta: { title: '小工具', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/system/actionlogs',
|
||||
name: 'SystemActionLogs',
|
||||
hidden: true,
|
||||
component: () => import('@/views/system/ActionLogs'),
|
||||
meta: { title: '操作日志', hiddenHeaderContent: false }
|
||||
},
|
||||
{
|
||||
path: '/system/about',
|
||||
name: 'About',
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
export const actionLogTypes = {
|
||||
BLOG_INITIALIZED: {
|
||||
value: 0,
|
||||
text: '博客初始化'
|
||||
},
|
||||
POST_PUBLISHED: {
|
||||
value: 5,
|
||||
text: '文章发布'
|
||||
},
|
||||
POST_EDITED: {
|
||||
value: 15,
|
||||
text: '文章修改'
|
||||
},
|
||||
POST_DELETED: {
|
||||
value: 20,
|
||||
text: '文章删除'
|
||||
},
|
||||
LOGGED_IN: {
|
||||
value: 25,
|
||||
text: '用户登录'
|
||||
},
|
||||
LOGGED_OUT: {
|
||||
value: 30,
|
||||
text: '注销登录'
|
||||
},
|
||||
LOGIN_FAILED: {
|
||||
value: 35,
|
||||
text: '登录失败'
|
||||
},
|
||||
PASSWORD_UPDATED: {
|
||||
value: 40,
|
||||
text: '修改密码'
|
||||
},
|
||||
PROFILE_UPDATED: {
|
||||
value: 45,
|
||||
text: '资料修改'
|
||||
},
|
||||
SHEET_PUBLISHED: {
|
||||
value: 50,
|
||||
text: '页面发布'
|
||||
},
|
||||
SHEET_EDITED: {
|
||||
value: 55,
|
||||
text: '页面修改'
|
||||
},
|
||||
SHEET_DELETED: {
|
||||
value: 60,
|
||||
text: '页面删除'
|
||||
},
|
||||
MFA_UPDATED: {
|
||||
value: 65,
|
||||
text: '两步验证'
|
||||
},
|
||||
LOGGED_PRE_CHECK: {
|
||||
value: 70,
|
||||
text: '登录验证'
|
||||
}
|
||||
}
|
|
@ -1 +1,3 @@
|
|||
@tailwind utilities;
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
|
|
@ -95,17 +95,17 @@
|
|||
<a-col :lg="8" :md="12" :sm="24" :xl="8" :xs="24" class="mb-3">
|
||||
<a-card :bodyStyle="{ padding: '16px' }" :bordered="false">
|
||||
<template slot="title">
|
||||
操作记录
|
||||
操作日志
|
||||
<a-tooltip slot="action" title="更多">
|
||||
<a href="javascript:void(0);" @click="logListDrawerVisible = true">
|
||||
<router-link :to="{ name: 'SystemActionLogs' }">
|
||||
<a-icon type="ellipsis" />
|
||||
</a>
|
||||
</router-link>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
<a-list :dataSource="formattedLogDatas" :loading="logLoading">
|
||||
<a-list :dataSource="latestLogs" :loading="logLoading">
|
||||
<a-list-item :key="index" slot="renderItem" slot-scope="item, index">
|
||||
<a-list-item-meta :description="item.createTime | timeAgo">
|
||||
<span slot="title">{{ item.type }}</span>
|
||||
<span slot="title">{{ item.type | typeConvert }}</span>
|
||||
</a-list-item-meta>
|
||||
<ellipsis :length="35" tooltip>{{ item.content }}</ellipsis>
|
||||
</a-list-item>
|
||||
|
@ -113,8 +113,6 @@
|
|||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<LogListDrawer :visible="logListDrawerVisible" @close="handleLogListClose" />
|
||||
</page-view>
|
||||
</template>
|
||||
|
||||
|
@ -123,9 +121,9 @@ import { PageView } from '@/layouts'
|
|||
import AnalysisCard from './components/AnalysisCard'
|
||||
import JournalPublishCard from './components/JournalPublishCard'
|
||||
import RecentCommentTab from './components/RecentCommentTab'
|
||||
import LogListDrawer from './components/LogListDrawer'
|
||||
|
||||
import apiClient from '@/utils/api-client'
|
||||
import { actionLogTypes } from '@/core/constant'
|
||||
|
||||
export default {
|
||||
name: 'Dashboard',
|
||||
|
@ -133,73 +131,13 @@ export default {
|
|||
PageView,
|
||||
AnalysisCard,
|
||||
JournalPublishCard,
|
||||
RecentCommentTab,
|
||||
LogListDrawer
|
||||
RecentCommentTab
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
logTypes: {
|
||||
BLOG_INITIALIZED: {
|
||||
value: 0,
|
||||
text: '博客初始化'
|
||||
},
|
||||
POST_PUBLISHED: {
|
||||
value: 5,
|
||||
text: '文章发布'
|
||||
},
|
||||
POST_EDITED: {
|
||||
value: 15,
|
||||
text: '文章修改'
|
||||
},
|
||||
POST_DELETED: {
|
||||
value: 20,
|
||||
text: '文章删除'
|
||||
},
|
||||
LOGGED_IN: {
|
||||
value: 25,
|
||||
text: '用户登录'
|
||||
},
|
||||
LOGGED_OUT: {
|
||||
value: 30,
|
||||
text: '注销登录'
|
||||
},
|
||||
LOGIN_FAILED: {
|
||||
value: 35,
|
||||
text: '登录失败'
|
||||
},
|
||||
PASSWORD_UPDATED: {
|
||||
value: 40,
|
||||
text: '修改密码'
|
||||
},
|
||||
PROFILE_UPDATED: {
|
||||
value: 45,
|
||||
text: '资料修改'
|
||||
},
|
||||
SHEET_PUBLISHED: {
|
||||
value: 50,
|
||||
text: '页面发布'
|
||||
},
|
||||
SHEET_EDITED: {
|
||||
value: 55,
|
||||
text: '页面修改'
|
||||
},
|
||||
SHEET_DELETED: {
|
||||
value: 60,
|
||||
text: '页面删除'
|
||||
},
|
||||
MFA_UPDATED: {
|
||||
value: 65,
|
||||
text: '两步验证'
|
||||
},
|
||||
LOGGED_PRE_CHECK: {
|
||||
value: 70,
|
||||
text: '登录验证'
|
||||
}
|
||||
},
|
||||
activityLoading: false,
|
||||
logLoading: false,
|
||||
statisticsLoading: true,
|
||||
logListDrawerVisible: false,
|
||||
latestPosts: [],
|
||||
latestLogs: [],
|
||||
statisticsData: {},
|
||||
|
@ -214,19 +152,6 @@ export default {
|
|||
this.handleListLatestPosts()
|
||||
this.handleListLatestLogs()
|
||||
},
|
||||
computed: {
|
||||
formattedLogDatas() {
|
||||
return this.latestLogs.map(log => {
|
||||
log.type = this.logTypes[log.type].text
|
||||
return log
|
||||
})
|
||||
}
|
||||
},
|
||||
destroyed: function () {
|
||||
if (this.logListDrawerVisible) {
|
||||
this.logListDrawerVisible = false
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
next(vm => {
|
||||
vm.interval = setInterval(() => {
|
||||
|
@ -240,9 +165,6 @@ export default {
|
|||
this.interval = null
|
||||
this.$log.debug('Cleared interval')
|
||||
}
|
||||
if (this.logListDrawerVisible) {
|
||||
this.logListDrawerVisible = false
|
||||
}
|
||||
next()
|
||||
},
|
||||
methods: {
|
||||
|
@ -285,10 +207,12 @@ export default {
|
|||
apiClient.post.getPreviewLinkById(postId).then(response => {
|
||||
window.open(response.data, '_blank')
|
||||
})
|
||||
},
|
||||
handleLogListClose() {
|
||||
this.logListDrawerVisible = false
|
||||
this.handleListLatestLogs()
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
typeConvert(key) {
|
||||
const type = actionLogTypes[key]
|
||||
return type ? type.text : key
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,187 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
<a-drawer
|
||||
:afterVisibleChange="handleAfterVisibleChanged"
|
||||
:visible="visible"
|
||||
:width="isMobile() ? '100%' : '480'"
|
||||
closable
|
||||
destroyOnClose
|
||||
title="操作日志"
|
||||
@close="onClose"
|
||||
>
|
||||
<a-row align="middle" type="flex">
|
||||
<a-col :span="24">
|
||||
<a-list :dataSource="formattedLogsDatas" :loading="loading">
|
||||
<a-list-item :key="index" slot="renderItem" slot-scope="item, index">
|
||||
<a-list-item-meta :description="item.createTime | timeAgo">
|
||||
<span slot="title">{{ item.type }}</span>
|
||||
</a-list-item-meta>
|
||||
<ellipsis :length="35" tooltip>{{ item.content }}</ellipsis>
|
||||
</a-list-item>
|
||||
</a-list>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<a-pagination
|
||||
:current="pagination.page"
|
||||
:defaultPageSize="pagination.size"
|
||||
:pageSizeOptions="['50', '100', '150', '200']"
|
||||
:total="pagination.total"
|
||||
class="pagination"
|
||||
showLessItems
|
||||
showSizeChanger
|
||||
@change="handlePaginationChange"
|
||||
@showSizeChange="handlePaginationChange"
|
||||
/>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
<a-divider class="divider-transparent" />
|
||||
<div class="bottom-control">
|
||||
<a-popconfirm cancelText="取消" okText="确定" title="你确定要清空所有操作日志?" @confirm="handleClearLogs">
|
||||
<a-button type="danger">清空操作日志</a-button>
|
||||
</a-popconfirm>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mixin, mixinDevice } from '@/mixins/mixin.js'
|
||||
import apiClient from '@/utils/api-client'
|
||||
|
||||
export default {
|
||||
name: 'LogListDrawer',
|
||||
mixins: [mixin, mixinDevice],
|
||||
data() {
|
||||
return {
|
||||
logTypes: {
|
||||
BLOG_INITIALIZED: {
|
||||
value: 0,
|
||||
text: '博客初始化'
|
||||
},
|
||||
POST_PUBLISHED: {
|
||||
value: 5,
|
||||
text: '文章发布'
|
||||
},
|
||||
POST_EDITED: {
|
||||
value: 15,
|
||||
text: '文章修改'
|
||||
},
|
||||
POST_DELETED: {
|
||||
value: 20,
|
||||
text: '文章删除'
|
||||
},
|
||||
LOGGED_IN: {
|
||||
value: 25,
|
||||
text: '用户登录'
|
||||
},
|
||||
LOGGED_OUT: {
|
||||
value: 30,
|
||||
text: '注销登录'
|
||||
},
|
||||
LOGIN_FAILED: {
|
||||
value: 35,
|
||||
text: '登录失败'
|
||||
},
|
||||
PASSWORD_UPDATED: {
|
||||
value: 40,
|
||||
text: '修改密码'
|
||||
},
|
||||
PROFILE_UPDATED: {
|
||||
value: 45,
|
||||
text: '资料修改'
|
||||
},
|
||||
SHEET_PUBLISHED: {
|
||||
value: 50,
|
||||
text: '页面发布'
|
||||
},
|
||||
SHEET_EDITED: {
|
||||
value: 55,
|
||||
text: '页面修改'
|
||||
},
|
||||
SHEET_DELETED: {
|
||||
value: 60,
|
||||
text: '页面删除'
|
||||
},
|
||||
MFA_UPDATED: {
|
||||
value: 65,
|
||||
text: '两步验证'
|
||||
},
|
||||
LOGGED_PRE_CHECK: {
|
||||
value: 70,
|
||||
text: '登录验证'
|
||||
}
|
||||
},
|
||||
loading: true,
|
||||
logs: [],
|
||||
pagination: {
|
||||
page: 1,
|
||||
size: 50,
|
||||
sort: null,
|
||||
total: 1
|
||||
},
|
||||
logQueryParam: {
|
||||
page: 0,
|
||||
size: 50,
|
||||
sort: null
|
||||
}
|
||||
}
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
formattedLogsDatas() {
|
||||
return this.logs.map(log => {
|
||||
log.type = this.logTypes[log.type].text
|
||||
return log
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleListLogs() {
|
||||
this.loading = true
|
||||
this.logQueryParam.page = this.pagination.page - 1
|
||||
this.logQueryParam.size = this.pagination.size
|
||||
this.logQueryParam.sort = this.pagination.sort
|
||||
apiClient.log
|
||||
.list(this.logQueryParam)
|
||||
.then(response => {
|
||||
this.logs = response.data.content
|
||||
this.pagination.total = response.data.total
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleClearLogs() {
|
||||
apiClient.log
|
||||
.clear()
|
||||
.then(() => {
|
||||
this.$message.success('清除成功!')
|
||||
})
|
||||
.finally(() => {
|
||||
this.handleListLogs()
|
||||
})
|
||||
},
|
||||
handlePaginationChange(page, pageSize) {
|
||||
this.$log.debug(`Current: ${page}, PageSize: ${pageSize}`)
|
||||
this.pagination.page = page
|
||||
this.pagination.size = pageSize
|
||||
this.handleListLogs()
|
||||
},
|
||||
onClose() {
|
||||
this.$emit('close', false)
|
||||
},
|
||||
handleAfterVisibleChanged(visible) {
|
||||
if (visible) {
|
||||
this.handleListLogs()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,171 @@
|
|||
<template>
|
||||
<page-view>
|
||||
<a-card :bodyStyle="{ padding: '16px' }" :bordered="false">
|
||||
<div class="table-operator">
|
||||
<a-button type="danger" @click="handleClearActionLogs">清空操作日志</a-button>
|
||||
</div>
|
||||
<div class="mt-4">
|
||||
<a-table
|
||||
:columns="list.columns"
|
||||
:dataSource="list.data"
|
||||
:loading="list.loading"
|
||||
:pagination="false"
|
||||
:rowKey="log => log.id"
|
||||
:scrollToFirstRowOnChange="true"
|
||||
>
|
||||
<template #type="type">
|
||||
{{ type | typeConvert }}
|
||||
</template>
|
||||
<template #ipAddress="ipAddress">
|
||||
<div class="blur hover:blur-none transition-all">{{ ipAddress }}</div>
|
||||
</template>
|
||||
<template #createTime="createTime">
|
||||
<a-tooltip placement="top">
|
||||
<template slot="title">
|
||||
{{ createTime | moment }}
|
||||
</template>
|
||||
{{ createTime | timeAgo }}
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-table>
|
||||
<div class="page-wrapper">
|
||||
<a-pagination
|
||||
:current="pagination.page"
|
||||
:defaultPageSize="pagination.size"
|
||||
:pageSizeOptions="['10', '20', '50', '100']"
|
||||
:total="pagination.total"
|
||||
class="pagination"
|
||||
showLessItems
|
||||
showSizeChanger
|
||||
@change="handlePageChange"
|
||||
@showSizeChange="handlePageSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</a-card>
|
||||
</page-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// components
|
||||
import { PageView } from '@/layouts'
|
||||
|
||||
// libs
|
||||
import apiClient from '@/utils/api-client'
|
||||
import { actionLogTypes } from '@/core/constant'
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: 'ID',
|
||||
dataIndex: 'id'
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'type',
|
||||
scopedSlots: { customRender: 'type' }
|
||||
},
|
||||
{
|
||||
title: '关键值',
|
||||
dataIndex: 'logKey'
|
||||
},
|
||||
{
|
||||
title: '内容',
|
||||
dataIndex: 'content'
|
||||
},
|
||||
{
|
||||
title: 'IP',
|
||||
dataIndex: 'ipAddress',
|
||||
scopedSlots: { customRender: 'ipAddress' }
|
||||
},
|
||||
{
|
||||
title: '操作时间',
|
||||
dataIndex: 'createTime',
|
||||
scopedSlots: { customRender: 'createTime' }
|
||||
}
|
||||
]
|
||||
|
||||
export default {
|
||||
name: 'ActionLog',
|
||||
components: {
|
||||
PageView
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: {
|
||||
columns,
|
||||
data: [],
|
||||
total: 0,
|
||||
loading: false,
|
||||
params: {
|
||||
page: 0,
|
||||
size: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pagination() {
|
||||
return {
|
||||
page: this.list.params.page + 1,
|
||||
size: this.list.params.size,
|
||||
total: this.list.total
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.handleListActionLogs()
|
||||
},
|
||||
methods: {
|
||||
async handleListActionLogs() {
|
||||
try {
|
||||
this.list.loading = true
|
||||
|
||||
const response = await apiClient.log.list(this.list.params)
|
||||
|
||||
this.list.data = response.data.content
|
||||
this.list.total = response.data.total
|
||||
} catch (error) {
|
||||
this.$log.error(error)
|
||||
} finally {
|
||||
this.list.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
handlePageChange(page = 1) {
|
||||
this.list.params.page = page - 1
|
||||
this.handleListActionLogs()
|
||||
},
|
||||
|
||||
handlePageSizeChange(current, size) {
|
||||
this.$log.debug(`Current: ${current}, PageSize: ${size}`)
|
||||
this.list.params.page = 0
|
||||
this.list.params.size = size
|
||||
this.handleListActionLogs()
|
||||
},
|
||||
|
||||
handleClearActionLogs() {
|
||||
const _this = this
|
||||
_this.$confirm({
|
||||
title: '提示',
|
||||
maskClosable: true,
|
||||
content: '是否确定要清空所有操作日志?',
|
||||
async onOk() {
|
||||
try {
|
||||
await apiClient.log.clear()
|
||||
} catch (e) {
|
||||
_this.$log.error('Failed to clear action logs.', e)
|
||||
} finally {
|
||||
await _this.handleListActionLogs()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
filters: {
|
||||
typeConvert(key) {
|
||||
const type = actionLogTypes[key]
|
||||
return type ? type.text : key
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -3,5 +3,8 @@ module.exports = {
|
|||
theme: {
|
||||
extend: {}
|
||||
},
|
||||
corePlugins: {
|
||||
preflight: false
|
||||
},
|
||||
plugins: []
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue