mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-30 09:42:48 +00:00
feat: Add Operate Log
This commit is contained in:
parent
8b52927b4f
commit
384e8f5f71
|
|
@ -0,0 +1,27 @@
|
|||
import { Result } from '@/request/Result'
|
||||
import { get } from '@/request/index'
|
||||
import type { pageRequest } from '@/api/type/common'
|
||||
import { type Ref } from 'vue'
|
||||
|
||||
const prefix = '/operate_log'
|
||||
/**
|
||||
* 日志分页列表
|
||||
* @param 参数
|
||||
* page {
|
||||
"current_page": "string",
|
||||
"page_size": "string",
|
||||
}
|
||||
* @query 参数
|
||||
param: any
|
||||
*/
|
||||
const getOperateLog: (
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (page, param, loading) => {
|
||||
return get(`${prefix}/${page.current_page}/${page.page_size}`, param, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
getOperateLog
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import problem from './problem'
|
|||
import log from './log'
|
||||
import applicationWorkflow from './application-workflow'
|
||||
import login from './login'
|
||||
import operateLog from './operate-log'
|
||||
export default {
|
||||
notFound,
|
||||
application,
|
||||
|
|
@ -28,5 +29,6 @@ export default {
|
|||
paragraph,
|
||||
problem,
|
||||
log,
|
||||
login
|
||||
login,
|
||||
operateLog
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
export default {
|
||||
title: 'Operate Logs',
|
||||
table: {
|
||||
menu: {
|
||||
label: 'Operate menu'
|
||||
},
|
||||
operate: {
|
||||
label: 'Operate'
|
||||
},
|
||||
user: {
|
||||
label: 'Operate user'
|
||||
},
|
||||
status: {
|
||||
label: 'Status',
|
||||
success: 'Successed',
|
||||
fail: 'Failed',
|
||||
all: 'All'
|
||||
},
|
||||
ip_address: {
|
||||
label: 'IP Address'
|
||||
},
|
||||
opt: {
|
||||
label: 'API Details'
|
||||
},
|
||||
operateTime: {
|
||||
label: 'Operate Time'
|
||||
}
|
||||
},
|
||||
close: 'Close'
|
||||
}
|
||||
|
|
@ -1,32 +1,34 @@
|
|||
import notFound from './404';
|
||||
import application from './application';
|
||||
import applicationOverview from './application-overview';
|
||||
import dataset from './dataset';
|
||||
import system from './system';
|
||||
import functionLib from './function-lib';
|
||||
import user from './user';
|
||||
import team from './team';
|
||||
import template from './template';
|
||||
import document from './document';
|
||||
import paragraph from './paragraph';
|
||||
import problem from './problem';
|
||||
import log from './log';
|
||||
import applicationWorkflow from './application-workflow';
|
||||
import login from './login';
|
||||
import notFound from './404'
|
||||
import application from './application'
|
||||
import applicationOverview from './application-overview'
|
||||
import dataset from './dataset'
|
||||
import system from './system'
|
||||
import functionLib from './function-lib'
|
||||
import user from './user'
|
||||
import team from './team'
|
||||
import template from './template'
|
||||
import document from './document'
|
||||
import paragraph from './paragraph'
|
||||
import problem from './problem'
|
||||
import log from './log'
|
||||
import applicationWorkflow from './application-workflow'
|
||||
import login from './login'
|
||||
import operateLog from './operate-log'
|
||||
export default {
|
||||
notFound,
|
||||
application,
|
||||
applicationOverview,
|
||||
dataset,
|
||||
system,
|
||||
functionLib,
|
||||
user,
|
||||
team,
|
||||
template,
|
||||
document,
|
||||
paragraph,
|
||||
problem,
|
||||
log,
|
||||
applicationWorkflow,
|
||||
login
|
||||
};
|
||||
notFound,
|
||||
application,
|
||||
applicationOverview,
|
||||
dataset,
|
||||
system,
|
||||
functionLib,
|
||||
user,
|
||||
team,
|
||||
template,
|
||||
document,
|
||||
paragraph,
|
||||
problem,
|
||||
log,
|
||||
applicationWorkflow,
|
||||
login,
|
||||
operateLog
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
export default {
|
||||
title: '操作日志',
|
||||
table: {
|
||||
menu: {
|
||||
label: '操作菜单'
|
||||
},
|
||||
operate: {
|
||||
label: '操作'
|
||||
},
|
||||
user: {
|
||||
label: '操作用户'
|
||||
},
|
||||
status: {
|
||||
label: '状态',
|
||||
success: '成功',
|
||||
fail: '失败',
|
||||
all: '全部'
|
||||
},
|
||||
ip_address: {
|
||||
label: 'IP地址'
|
||||
},
|
||||
opt: {
|
||||
label: 'API详情'
|
||||
},
|
||||
operateTime: {
|
||||
label: '操作时间'
|
||||
}
|
||||
},
|
||||
close: '关闭'
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import problem from './problem'
|
|||
import log from './log'
|
||||
import applicationWorkflow from './application-workflow'
|
||||
import login from './login'
|
||||
import operateLog from './operate-log'
|
||||
export default {
|
||||
notFound,
|
||||
application,
|
||||
|
|
@ -28,5 +29,6 @@ export default {
|
|||
paragraph,
|
||||
problem,
|
||||
log,
|
||||
login
|
||||
login,
|
||||
operateLog
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
export default {
|
||||
title: '操作日誌',
|
||||
table: {
|
||||
menu: {
|
||||
label: '操作菜單'
|
||||
},
|
||||
operate: {
|
||||
label: '操作'
|
||||
},
|
||||
user: {
|
||||
label: '操作用戶'
|
||||
},
|
||||
status: {
|
||||
label: '狀態',
|
||||
success: '成功',
|
||||
fail: '失敗',
|
||||
all: '全部'
|
||||
},
|
||||
ip_address: {
|
||||
label: 'IP地址'
|
||||
},
|
||||
opt: {
|
||||
label: 'API詳情'
|
||||
},
|
||||
operateTime: {
|
||||
label: '操作時間'
|
||||
}
|
||||
},
|
||||
close: '關閉'
|
||||
}
|
||||
|
|
@ -103,6 +103,20 @@ const settingRouter = {
|
|||
component: () => import('@/views/email/index.vue')
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/operate',
|
||||
name: 'operate',
|
||||
meta: {
|
||||
icon: 'app-document',
|
||||
iconActive: 'app-document-active',
|
||||
title: 'views.operateLog.title',
|
||||
activeMenu: '/setting',
|
||||
parentPath: '/setting',
|
||||
parentName: 'setting',
|
||||
permission: new ComplexPermission(['ADMIN'], ['x-pack'], 'AND')
|
||||
},
|
||||
component: () => import('@/views/operate-log/index.vue')
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="$t('views.operateLog.table.opt.label')"
|
||||
v-model="dialogVisible"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
<el-scrollbar height="400" class="details">
|
||||
<div class="content">{{ details }}</div>
|
||||
</el-scrollbar>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click.prevent="dialogVisible = false">
|
||||
{{ $t('views.operateLog.close') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const details = ref<string>()
|
||||
|
||||
const open = (data: any) => {
|
||||
details.value = JSON.stringify(data.details, null, 4)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
|
||||
defineExpose({ open })
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.details {
|
||||
margin: 0 10px 30px;
|
||||
border: 1px #cccccc solid;
|
||||
.content {
|
||||
padding: 10px 20px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,259 @@
|
|||
<template>
|
||||
<LayoutContainer :header="$t('views.operateLog.title')">
|
||||
<div class="p-24">
|
||||
<div class="flex-between">
|
||||
<div>
|
||||
<el-select
|
||||
v-model="history_day"
|
||||
class="mr-12"
|
||||
@change="changeDayHandle"
|
||||
style="width: 180px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in dayOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
v-if="history_day === 'other'"
|
||||
v-model="daterangeValue"
|
||||
type="daterange"
|
||||
:start-placeholder="$t('views.applicationOverview.monitor.startDatePlaceholder')"
|
||||
:end-placeholder="$t('views.applicationOverview.monitor.endDatePlaceholder')"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
@change="changeDayRangeHandle"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex-between complex-search">
|
||||
<el-select
|
||||
v-model="filter_type"
|
||||
class="complex-search__left"
|
||||
@change="changeFilterHandle"
|
||||
style="width: 120px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in filterOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-select
|
||||
v-if="filter_type === 'status'"
|
||||
v-model="filter_status"
|
||||
@change="changeStatusHandle"
|
||||
style="width: 220px"
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in statusOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="searchValue"
|
||||
@change="getList"
|
||||
:placeholder="$t('common.search')"
|
||||
prefix-icon="Search"
|
||||
style="width: 220px"
|
||||
clearable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<app-table
|
||||
class="mt-16"
|
||||
:data="tableData"
|
||||
:pagination-config="paginationConfig"
|
||||
@sizeChange="handleSizeChange"
|
||||
@changePage="getList"
|
||||
v-loading="loading"
|
||||
>
|
||||
<el-table-column prop="menu" :label="$t('views.operateLog.table.menu.label')" width="160" />
|
||||
<el-table-column prop="operate" :label="$t('views.operateLog.table.operate.label')" />
|
||||
<el-table-column
|
||||
width="120"
|
||||
prop="user.username"
|
||||
:label="$t('views.operateLog.table.user.label')"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="status"
|
||||
:label="$t('views.operateLog.table.status.label')"
|
||||
width="100"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.status === 200">{{ $t('views.operateLog.table.status.success') }}</span>
|
||||
<span v-else style="color: red">{{ $t('views.operateLog.table.status.fail') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="ip_address"
|
||||
:label="$t('views.operateLog.table.ip_address.label')"
|
||||
width="160"
|
||||
></el-table-column>
|
||||
<el-table-column :label="$t('views.operateLog.table.operateTime.label')" width="180">
|
||||
<template #default="{ row }">
|
||||
{{ datetimeFormat(row.create_time) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('common.operation')" width="110" align="left" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<span class="mr-4">
|
||||
<el-button type="primary" text @click.stop="showDetails(row)" class="text-button">
|
||||
{{ $t('views.operateLog.table.opt.label') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</app-table>
|
||||
</div>
|
||||
<DetailDialog ref="DetailDialogRef" />
|
||||
</LayoutContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import getOperateLog from '@/api/operate-log'
|
||||
import DetailDialog from './component/DetailDialog.vue'
|
||||
import { t } from '@/locales'
|
||||
import { beforeDay, datetimeFormat, nowDate } from '@/utils/time'
|
||||
|
||||
const DetailDialogRef = ref()
|
||||
const loading = ref(false)
|
||||
const paginationConfig = reactive({
|
||||
current_page: 1,
|
||||
page_size: 20,
|
||||
total: 0
|
||||
})
|
||||
const searchValue = ref('')
|
||||
const tableData = ref<any[]>([])
|
||||
const history_day = ref<number | string>(7)
|
||||
const filter_type = ref<string>('menu')
|
||||
const filter_status = ref<string>('')
|
||||
const daterange = ref({
|
||||
start_time: '',
|
||||
end_time: ''
|
||||
})
|
||||
const daterangeValue = ref('')
|
||||
const dayOptions = [
|
||||
{
|
||||
value: 7,
|
||||
// @ts-ignore
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past7Days') // 使用 t 方法来国际化显示文本
|
||||
},
|
||||
{
|
||||
value: 30,
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past30Days')
|
||||
},
|
||||
{
|
||||
value: 90,
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past90Days')
|
||||
},
|
||||
{
|
||||
value: 183,
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.past183Days')
|
||||
},
|
||||
{
|
||||
value: 'other',
|
||||
label: t('views.applicationOverview.monitor.pastDayOptions.other')
|
||||
}
|
||||
]
|
||||
const filterOptions = [
|
||||
{
|
||||
value: 'menu',
|
||||
label: t('views.operateLog.table.menu.label')
|
||||
},
|
||||
{
|
||||
value: 'operate',
|
||||
label: t('views.operateLog.table.operate.label')
|
||||
},
|
||||
{
|
||||
value: 'user',
|
||||
label: t('views.operateLog.table.user.label')
|
||||
},
|
||||
{
|
||||
value: 'status',
|
||||
label: t('views.operateLog.table.status.label')
|
||||
},
|
||||
{
|
||||
value: 'ip_address',
|
||||
label: t('views.operateLog.table.ip_address.label')
|
||||
}
|
||||
]
|
||||
const statusOptions = [
|
||||
{
|
||||
value: '200',
|
||||
label: t('views.operateLog.table.status.success')
|
||||
},
|
||||
{
|
||||
value: '500',
|
||||
label: t('views.operateLog.table.status.fail')
|
||||
}
|
||||
]
|
||||
|
||||
function changeStatusHandle(val: string) {
|
||||
getList()
|
||||
}
|
||||
|
||||
function changeFilterHandle(val: string) {
|
||||
filter_type.value = val
|
||||
if (searchValue.value) {
|
||||
getList()
|
||||
}
|
||||
}
|
||||
|
||||
function changeDayHandle(val: number | string) {
|
||||
if (val !== 'other') {
|
||||
daterange.value.start_time = beforeDay(val)
|
||||
daterange.value.end_time = nowDate
|
||||
getList()
|
||||
}
|
||||
}
|
||||
function changeDayRangeHandle(val: string) {
|
||||
daterange.value.start_time = val[0]
|
||||
daterange.value.end_time = val[1]
|
||||
getList()
|
||||
}
|
||||
|
||||
function showDetails(row: any) {
|
||||
DetailDialogRef.value.open(row)
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
paginationConfig.current_page = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
let obj: any = {
|
||||
start_time: daterange.value.start_time,
|
||||
end_time: daterange.value.end_time
|
||||
}
|
||||
if (searchValue.value && filter_type.value !== 'status') {
|
||||
obj[filter_type.value] = searchValue.value
|
||||
}
|
||||
if (filter_type.value === 'status') {
|
||||
obj['status'] = filter_status.value
|
||||
}
|
||||
return getOperateLog.getOperateLog(paginationConfig, obj, loading).then((res) => {
|
||||
tableData.value = res.data.records
|
||||
paginationConfig.total = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
changeDayHandle(history_day.value)
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.text-button {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
Loading…
Reference in New Issue