mirror of
https://github.com/chansee97/nova-admin.git
synced 2025-09-19 04:49:58 +08:00
feat: 增加日志模块
This commit is contained in:
parent
d6c70bad0a
commit
d400ebedc0
@ -55,7 +55,7 @@
|
||||
"md-editor-v3": "^5.6.1",
|
||||
"pinia": "^3.0.3",
|
||||
"pinia-plugin-persistedstate": "^4.5.0",
|
||||
"pro-naive-ui": "^3.0.3",
|
||||
"pro-naive-ui": "^3.1.1",
|
||||
"quill": "^2.0.3",
|
||||
"radash": "^12.1.1",
|
||||
"vue": "^3.5.20",
|
||||
|
@ -20,6 +20,10 @@ const propOverrides = {
|
||||
preset: 'card',
|
||||
},
|
||||
ProDataTable: {
|
||||
tableCardProps: {
|
||||
size: 'small',
|
||||
},
|
||||
size: 'small',
|
||||
paginateSinglePage: false,
|
||||
},
|
||||
}
|
||||
|
31
src/api/monitor/login-log.ts
Normal file
31
src/api/monitor/login-log.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { request } from '@/utils/alova'
|
||||
|
||||
export type LoginLogSearchQuery = Partial<Entity.LoginLog> & Api.PageParams
|
||||
|
||||
/**
|
||||
* 分页查询登录日志列表
|
||||
*/
|
||||
export function getLoginLogPage(params: LoginLogSearchQuery) {
|
||||
return request.Get<Api.ListResponse<Entity.LoginLog>>('/login-log', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取登录日志详情
|
||||
*/
|
||||
export function getLoginLogDetail(id: number) {
|
||||
return request.Get<Entity.LoginLog>(`/login-log/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除登录日志
|
||||
*/
|
||||
export function deleteLoginLog(ids: string) {
|
||||
return request.Delete(`/login-log/${ids}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空登录日志
|
||||
*/
|
||||
export function clearLoginLog() {
|
||||
return request.Delete('/login-log/clean')
|
||||
}
|
31
src/api/monitor/oper-log.ts
Normal file
31
src/api/monitor/oper-log.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { request } from '@/utils/alova'
|
||||
|
||||
export interface OperLogQueryParams extends Api.PageParams {
|
||||
}
|
||||
/**
|
||||
* 分页查询操作日志列表
|
||||
*/
|
||||
export function getOperLogPage(params: OperLogQueryParams) {
|
||||
return request.Get<Api.ListResponse<Entity.OperLog>>('/oper-log', { params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作日志详情
|
||||
*/
|
||||
export function getOperLogDetail(id: number) {
|
||||
return request.Get<Entity.OperLog>(`/oper-log/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除操作日志
|
||||
*/
|
||||
export function deleteOperLog(ids: string) {
|
||||
return request.Delete(`/oper-log/${ids}`)
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空操作日志
|
||||
*/
|
||||
export function clearOperLog() {
|
||||
return request.Delete('/oper-log/clean')
|
||||
}
|
@ -1,12 +1,14 @@
|
||||
import {
|
||||
create,
|
||||
ProDateRange,
|
||||
ProDateTimeRange,
|
||||
ProInput,
|
||||
ProSelect,
|
||||
} from 'pro-naive-ui'
|
||||
import type { App } from 'vue'
|
||||
|
||||
const proNaive = create({
|
||||
components: [ProInput, ProSelect],
|
||||
components: [ProInput, ProSelect, ProDateRange, ProDateTimeRange],
|
||||
})
|
||||
|
||||
export function install(app: App) {
|
||||
|
@ -127,7 +127,7 @@ export function setupRouterGuard(router: Router) {
|
||||
|
||||
router.beforeResolve((to) => {
|
||||
// 设置菜单高亮
|
||||
routeStore.setActiveMenu(to.meta.activePath ?? to.fullPath)
|
||||
routeStore.setActiveMenu(to.meta.activePath || to.fullPath)
|
||||
// 添加tabs
|
||||
tabStore.addTab(to)
|
||||
// 设置高亮标签
|
||||
|
43
src/typings/entities/login-log.d.ts
vendored
Normal file
43
src/typings/entities/login-log.d.ts
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
declare namespace Entity {
|
||||
/**
|
||||
* 登录日志
|
||||
*/
|
||||
interface LoginLog {
|
||||
/**
|
||||
* 日志编号
|
||||
*/
|
||||
id: number
|
||||
/**
|
||||
* 用户账号
|
||||
*/
|
||||
username: string
|
||||
/**
|
||||
* 登录IP地址
|
||||
*/
|
||||
ipaddr: string
|
||||
/**
|
||||
* 登录地点
|
||||
*/
|
||||
loginLocation: string
|
||||
/**
|
||||
* 浏览器类型
|
||||
*/
|
||||
browser: string
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
os: string
|
||||
/**
|
||||
* 登录状态(0成功 1失败)
|
||||
*/
|
||||
status: number
|
||||
/**
|
||||
* 提示消息
|
||||
*/
|
||||
msg: string
|
||||
/**
|
||||
* 访问时间
|
||||
*/
|
||||
loginTime: string
|
||||
}
|
||||
}
|
75
src/typings/entities/oper-log.d.ts
vendored
Normal file
75
src/typings/entities/oper-log.d.ts
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
declare namespace Entity {
|
||||
/**
|
||||
* 操作日志
|
||||
*/
|
||||
interface OperLog {
|
||||
/**
|
||||
* 日志编号
|
||||
*/
|
||||
id: number
|
||||
/**
|
||||
* 模块标题
|
||||
*/
|
||||
title: string
|
||||
/**
|
||||
* 方法名称
|
||||
*/
|
||||
method: string
|
||||
/**
|
||||
* 请求方式
|
||||
*/
|
||||
requestMethod: string
|
||||
/**
|
||||
* 操作人员
|
||||
*/
|
||||
operName: string
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
deptName: string
|
||||
/**
|
||||
* 请求URL
|
||||
*/
|
||||
operUrl: string
|
||||
/**
|
||||
* 主机地址
|
||||
*/
|
||||
operIp: string
|
||||
/**
|
||||
* 操作地点
|
||||
*/
|
||||
operLocation: string
|
||||
/**
|
||||
* 浏览器类型
|
||||
*/
|
||||
browser: string
|
||||
/**
|
||||
* 操作系统
|
||||
*/
|
||||
os: string
|
||||
/**
|
||||
* 请求参数
|
||||
*/
|
||||
operParam: string
|
||||
/**
|
||||
* 返回参数
|
||||
*/
|
||||
jsonResult: string
|
||||
/**
|
||||
* 操作状态(0正常 1异常)
|
||||
*/
|
||||
status: number
|
||||
/**
|
||||
* 错误消息
|
||||
*/
|
||||
errorMsg: string
|
||||
/**
|
||||
* 消耗时间
|
||||
*/
|
||||
costTime: string
|
||||
/**
|
||||
* 操作时间
|
||||
*/
|
||||
operTime: Date
|
||||
}
|
||||
}
|
5
src/typings/global.d.ts
vendored
5
src/typings/global.d.ts
vendored
@ -9,6 +9,11 @@ declare namespace Entity {
|
||||
|
||||
/* 各类接口返回的数据类型, 具体内容在 ./api */
|
||||
declare namespace Api {
|
||||
interface PageParams {
|
||||
pageNum: number
|
||||
pageSize: number
|
||||
}
|
||||
|
||||
interface Response<T> {
|
||||
/** 业务状态码 */
|
||||
code: number
|
||||
|
155
src/views/monitor/login-log/columns.tsx
Normal file
155
src/views/monitor/login-log/columns.tsx
Normal file
@ -0,0 +1,155 @@
|
||||
import type { DataTableColumns } from 'naive-ui'
|
||||
import { NButton, NSpace, NTag } from 'naive-ui'
|
||||
import type { ProSearchFormColumns } from 'pro-naive-ui'
|
||||
import { renderProCopyableText } from 'pro-naive-ui'
|
||||
|
||||
// 登录日志搜索表单数据类型
|
||||
export interface LoginLogSearchFormData {
|
||||
ipaddr?: string
|
||||
userName?: string
|
||||
status?: number
|
||||
loginTime?: string
|
||||
}
|
||||
|
||||
// 登录日志搜索表单列配置
|
||||
export const searchColumns: ProSearchFormColumns<LoginLogSearchFormData> = [
|
||||
{
|
||||
title: '登录地址',
|
||||
path: 'ipaddr',
|
||||
field: 'input',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '用户名称',
|
||||
path: 'username',
|
||||
field: 'input',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
path: 'status',
|
||||
field: 'select',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
options: [
|
||||
{ label: '成功', value: '0' },
|
||||
{ label: '失败', value: '1' },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '登录时间',
|
||||
path: 'loginTime',
|
||||
field: 'date-time-range',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
format: 'yyyy-MM-dd HH:mm:ss',
|
||||
valueFormat: 'yyyy-MM-dd HH:mm:ss',
|
||||
defaultTime: ['00:00:00', '23:59:59'],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// 表格列配置
|
||||
export function createTableColumns(options: {
|
||||
onDelete: (infoId: number) => void
|
||||
}): DataTableColumns<Entity.LoginLog> {
|
||||
const { onDelete } = options
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
},
|
||||
{
|
||||
title: '用户名称',
|
||||
key: 'username',
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '登录地址',
|
||||
key: 'ipaddr',
|
||||
width: 130,
|
||||
align: 'center',
|
||||
render: row => renderProCopyableText(row.ipaddr),
|
||||
},
|
||||
{
|
||||
title: '登录地点',
|
||||
key: 'loginLocation',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '浏览器',
|
||||
key: 'browser',
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作系统',
|
||||
key: 'os',
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '登录状态',
|
||||
key: 'status',
|
||||
width: 80,
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return (
|
||||
<NTag type={row.status === 0 ? 'success' : 'error'} bordered={false}>
|
||||
{row.status === 0 ? '成功' : '失败'}
|
||||
</NTag>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作信息',
|
||||
key: 'msg',
|
||||
width: 200,
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '登录日期',
|
||||
key: 'loginTime',
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
render: (row) => {
|
||||
return (
|
||||
<NSpace justify="center">
|
||||
<NButton
|
||||
text
|
||||
type="error"
|
||||
onClick={() => onDelete(row.id)}
|
||||
>
|
||||
删除
|
||||
</NButton>
|
||||
</NSpace>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
149
src/views/monitor/login-log/index.vue
Normal file
149
src/views/monitor/login-log/index.vue
Normal file
@ -0,0 +1,149 @@
|
||||
<script setup lang="tsx">
|
||||
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
|
||||
import {
|
||||
clearLoginLog,
|
||||
deleteLoginLog,
|
||||
getLoginLogPage,
|
||||
} from '@/api/monitor/login-log'
|
||||
import type { LoginLogSearchQuery } from '@/api/monitor/login-log'
|
||||
import { createTableColumns, searchColumns } from './columns'
|
||||
import type { LoginLogSearchFormData } from './columns'
|
||||
|
||||
// 创建搜索表单
|
||||
const searchForm = createProSearchForm<LoginLogSearchFormData>({
|
||||
defaultCollapsed: true,
|
||||
initialValues: {
|
||||
},
|
||||
})
|
||||
|
||||
// 使用useNDataTable
|
||||
const {
|
||||
table: {
|
||||
tableProps,
|
||||
},
|
||||
search: {
|
||||
proSearchFormProps,
|
||||
searchLoading,
|
||||
},
|
||||
refresh,
|
||||
} = useNDataTable(getList, {
|
||||
form: searchForm,
|
||||
})
|
||||
|
||||
// 批量删除相关
|
||||
const checkedRowKeys = ref<number[]>([])
|
||||
|
||||
/** 查询登录日志列表 */
|
||||
async function getList({ current, pageSize }: any, formData: LoginLogSearchFormData) {
|
||||
try {
|
||||
const params: LoginLogSearchQuery = {
|
||||
pageNum: current,
|
||||
pageSize,
|
||||
...formData,
|
||||
}
|
||||
|
||||
return getLoginLogPage(params).then(res => res.data)
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取登录日志列表失败:', error)
|
||||
return {
|
||||
total: 0,
|
||||
list: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 删除登录日志 */
|
||||
async function handleDelete(infoId: number | number[]) {
|
||||
const isBatch = Array.isArray(infoId)
|
||||
const ids = isBatch ? infoId.join(',') : infoId.toString()
|
||||
const count = isBatch ? infoId.length : 1
|
||||
|
||||
window.$dialog.warning({
|
||||
title: '确认删除',
|
||||
content: isBatch
|
||||
? `是否确认删除选中的 ${count} 条登录日志?`
|
||||
: '是否确认删除该条登录日志?',
|
||||
positiveText: '确定删除',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await deleteLoginLog(ids)
|
||||
window.$message.success('删除成功')
|
||||
if (isBatch) {
|
||||
// 清空选中项
|
||||
checkedRowKeys.value = []
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
catch (error) {
|
||||
console.error('删除登录日志失败:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空登录日志 */
|
||||
async function handleClean() {
|
||||
window.$dialog.warning({
|
||||
title: '确认清空',
|
||||
content: '是否确认清空所有登录日志数据?此操作不可恢复!',
|
||||
positiveText: '确定清空',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await clearLoginLog()
|
||||
window.$message.success('清空成功')
|
||||
refresh()
|
||||
}
|
||||
catch (error) {
|
||||
console.error('清空登录日志失败:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// 创建表格列配置
|
||||
const columns = createTableColumns({
|
||||
onDelete: handleDelete,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-card>
|
||||
<pro-search-form
|
||||
v-bind="proSearchFormProps"
|
||||
:form="searchForm"
|
||||
:columns="searchColumns"
|
||||
/>
|
||||
</n-card>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<pro-data-table
|
||||
v-bind="tableProps"
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
:loading="searchLoading"
|
||||
title=" "
|
||||
>
|
||||
<template #toolbar>
|
||||
<n-flex>
|
||||
<n-button type="error" :disabled="checkedRowKeys.length === 0" @click="handleDelete(checkedRowKeys)">
|
||||
<template #icon>
|
||||
<icon-park-outline-delete />
|
||||
</template>
|
||||
删除
|
||||
</n-button>
|
||||
<n-button type="warning" @click="handleClean">
|
||||
<template #icon>
|
||||
<icon-park-outline-clear />
|
||||
</template>
|
||||
清空
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
</pro-data-table>
|
||||
</n-space>
|
||||
</template>
|
168
src/views/monitor/oper-log/columns.tsx
Normal file
168
src/views/monitor/oper-log/columns.tsx
Normal file
@ -0,0 +1,168 @@
|
||||
import type { DataTableColumns } from 'naive-ui'
|
||||
import { NButton, NSpace, NTag } from 'naive-ui'
|
||||
import type { ProSearchFormColumns } from 'pro-naive-ui'
|
||||
import { renderProCopyableText } from 'pro-naive-ui'
|
||||
|
||||
// 操作日志搜索表单数据类型
|
||||
export interface OperationLogSearchFormData {
|
||||
operUrl?: string
|
||||
title?: string
|
||||
operName?: string
|
||||
businessType?: number
|
||||
status?: 0 | 1
|
||||
operTime?: [string, string]
|
||||
}
|
||||
|
||||
// 操作日志搜索表单列配置
|
||||
export const searchColumns: ProSearchFormColumns<OperationLogSearchFormData> = [
|
||||
{
|
||||
title: '操作地址',
|
||||
path: 'operUrl',
|
||||
field: 'input',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '系统模块',
|
||||
path: 'title',
|
||||
field: 'input',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作人员',
|
||||
path: 'operName',
|
||||
field: 'input',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
path: 'status',
|
||||
field: 'select',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
options: [
|
||||
{ label: '正常', value: 0 },
|
||||
{ label: '异常', value: 1 },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作时间',
|
||||
path: 'operTime',
|
||||
field: 'date-time-range',
|
||||
fieldProps: {
|
||||
clearable: true,
|
||||
format: 'yyyy-MM-dd',
|
||||
valueFormat: 'yyyy-MM-dd HH:mm:ss',
|
||||
defaultTime: ['00:00:00', '23:59:59'],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
// 表格列配置
|
||||
export function createTableColumns(options: {
|
||||
onView: (row: Entity.OperLog) => void
|
||||
onDelete: (operId: number) => void
|
||||
}): DataTableColumns<Entity.OperLog> {
|
||||
const { onView, onDelete } = options
|
||||
|
||||
return [
|
||||
{
|
||||
type: 'selection',
|
||||
width: 55,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '系统模块',
|
||||
key: 'title',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作人员',
|
||||
key: 'operName',
|
||||
width: 120,
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '主机地址',
|
||||
key: 'operIp',
|
||||
width: 130,
|
||||
align: 'center',
|
||||
render: row => renderProCopyableText(row.operIp),
|
||||
},
|
||||
{
|
||||
title: '操作地点',
|
||||
key: 'operLocation',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
ellipsis: {
|
||||
tooltip: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作状态',
|
||||
key: 'status',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
render: (row) => {
|
||||
return (
|
||||
<NTag type={row.status === 0 ? 'success' : 'error'} bordered={false}>
|
||||
{row.status === 0 ? '正常' : '异常'}
|
||||
</NTag>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作日期',
|
||||
key: 'operTime',
|
||||
width: 180,
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '消耗时间',
|
||||
key: 'costTime',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
render: row => `${row.costTime}`,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'actions',
|
||||
width: 150,
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
render: (row) => {
|
||||
return (
|
||||
<NSpace justify="center">
|
||||
<NButton
|
||||
text
|
||||
type="primary"
|
||||
onClick={() => onView(row)}
|
||||
>
|
||||
详细
|
||||
</NButton>
|
||||
<NButton
|
||||
text
|
||||
type="error"
|
||||
onClick={() => onDelete(row.id)}
|
||||
>
|
||||
删除
|
||||
</NButton>
|
||||
</NSpace>
|
||||
)
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
119
src/views/monitor/oper-log/components/DetailModal.vue
Normal file
119
src/views/monitor/oper-log/components/DetailModal.vue
Normal file
@ -0,0 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import { useBoolean } from '@/hooks'
|
||||
|
||||
const { bool: visible, setTrue: showModal, setFalse: hideModal } = useBoolean(false)
|
||||
|
||||
const operationLog = ref<Entity.OperLog | null>(null)
|
||||
|
||||
// 打开弹窗
|
||||
function openModal(row: Entity.OperLog) {
|
||||
operationLog.value = row
|
||||
showModal()
|
||||
}
|
||||
|
||||
// 关闭弹窗
|
||||
function closeModal() {
|
||||
hideModal()
|
||||
operationLog.value = null
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
openModal,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-modal
|
||||
v-model:show="visible"
|
||||
preset="card"
|
||||
title="操作日志详细"
|
||||
class="w-4/5 max-w-4xl"
|
||||
:mask-closable="false"
|
||||
>
|
||||
<div v-if="operationLog" class="space-y-4">
|
||||
<!-- 基本信息 -->
|
||||
<n-card title="基本信息" size="small">
|
||||
<n-descriptions :column="3" label-placement="left" size="small">
|
||||
<n-descriptions-item label="日志编号">
|
||||
{{ operationLog.id }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="系统模块">
|
||||
{{ operationLog.title }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="部门名称">
|
||||
{{ operationLog.deptName || '-' }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="操作状态">
|
||||
<n-tag size="small">
|
||||
{{ operationLog.status === 0 ? '正常' : '异常' }}
|
||||
</n-tag>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="消耗时间">
|
||||
{{ `${operationLog.costTime}ms` }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="操作时间">
|
||||
{{ operationLog.operTime }}
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
|
||||
<!-- 请求信息 -->
|
||||
<n-card title="请求信息" size="small">
|
||||
<n-descriptions :column="2" label-placement="left" size="small">
|
||||
<n-descriptions-item label="调用方法" :span="2">
|
||||
<n-text code>
|
||||
{{ operationLog.method }}
|
||||
</n-text>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="请求地址">
|
||||
<n-text code>
|
||||
{{ operationLog.operUrl }}
|
||||
</n-text>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="请求方式">
|
||||
<n-tag size="small">
|
||||
{{ operationLog.requestMethod }}
|
||||
</n-tag>
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="主机地址">
|
||||
{{ operationLog.operIp }}
|
||||
</n-descriptions-item>
|
||||
<n-descriptions-item label="操作地点">
|
||||
{{ operationLog.operLocation }}
|
||||
</n-descriptions-item>
|
||||
</n-descriptions>
|
||||
</n-card>
|
||||
|
||||
<!-- 请求参数 -->
|
||||
<pro-card title="请求参数" size="small" :show="false">
|
||||
<n-scrollbar style="max-height: 200px">
|
||||
<pre>{{ JSON.parse(operationLog.operParam) }}</pre>
|
||||
</n-scrollbar>
|
||||
</pro-card>
|
||||
|
||||
<!-- 返回结果 -->
|
||||
<pro-card title="返回结果" size="small">
|
||||
<n-scrollbar style="max-height: 200px">
|
||||
<pre>{{ operationLog.jsonResult }}</pre>
|
||||
</n-scrollbar>
|
||||
</pro-card>
|
||||
|
||||
<!-- 异常信息 -->
|
||||
<n-card v-if="operationLog.status === 1" title="异常信息" size="small">
|
||||
<n-scrollbar style="max-height: 200px">
|
||||
<n-text type="error">
|
||||
{{ operationLog.errorMsg || '无异常信息' }}
|
||||
</n-text>
|
||||
</n-scrollbar>
|
||||
</n-card>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end">
|
||||
<n-button @click="closeModal">
|
||||
关闭
|
||||
</n-button>
|
||||
</div>
|
||||
</template>
|
||||
</n-modal>
|
||||
</template>
|
170
src/views/monitor/oper-log/index.vue
Normal file
170
src/views/monitor/oper-log/index.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<script setup lang="tsx">
|
||||
import { createProSearchForm, useNDataTable } from 'pro-naive-ui'
|
||||
import {
|
||||
clearOperLog,
|
||||
deleteOperLog,
|
||||
getOperLogPage,
|
||||
} from '@/api/monitor/oper-log'
|
||||
import type { OperLogQueryParams } from '@/api/monitor/oper-log'
|
||||
import { createTableColumns, searchColumns } from './columns'
|
||||
import type { OperationLogSearchFormData } from './columns'
|
||||
|
||||
// 导入子组件
|
||||
import DetailModal from './components/DetailModal.vue'
|
||||
|
||||
// 弹窗引用
|
||||
const detailModalRef = ref()
|
||||
|
||||
// 创建搜索表单
|
||||
const searchForm = createProSearchForm<OperationLogSearchFormData>({
|
||||
defaultCollapsed: true,
|
||||
initialValues: {
|
||||
},
|
||||
})
|
||||
|
||||
// 使用useNDataTable
|
||||
const {
|
||||
table: {
|
||||
tableProps,
|
||||
},
|
||||
search: {
|
||||
proSearchFormProps,
|
||||
searchLoading,
|
||||
},
|
||||
refresh,
|
||||
} = useNDataTable(getList, {
|
||||
form: searchForm,
|
||||
})
|
||||
|
||||
// 批量删除相关
|
||||
const checkedRowKeys = ref<(number)[]>([])
|
||||
|
||||
/** 查询操作日志列表 */
|
||||
interface Result {
|
||||
total: number
|
||||
list: Entity.OperLog[]
|
||||
}
|
||||
async function getList({ current, pageSize }: any, formData: OperationLogSearchFormData): Promise<Result> {
|
||||
try {
|
||||
const params: OperLogQueryParams = {
|
||||
pageNum: current,
|
||||
pageSize,
|
||||
...formData,
|
||||
}
|
||||
|
||||
return getOperLogPage(params).then(res => res.data)
|
||||
}
|
||||
catch (error) {
|
||||
console.error('获取操作日志列表失败:', error)
|
||||
return {
|
||||
total: 0,
|
||||
list: [],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
function handleView(row: Entity.OperLog) {
|
||||
detailModalRef.value?.openModal(row)
|
||||
}
|
||||
|
||||
/** 删除操作日志 */
|
||||
async function handleDelete(operId: number | number[]) {
|
||||
const isBatch = Array.isArray(operId)
|
||||
const ids = isBatch ? operId.join(',') : operId.toString()
|
||||
|
||||
window.$dialog.warning({
|
||||
title: '确认删除',
|
||||
content: isBatch
|
||||
? `是否确认删除选中的 ${operId.length} 条操作日志?`
|
||||
: '是否确认删除该条操作日志?',
|
||||
positiveText: '确定删除',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await deleteOperLog(ids)
|
||||
window.$message.success('删除成功')
|
||||
if (isBatch) {
|
||||
// 清空选中项
|
||||
checkedRowKeys.value = []
|
||||
}
|
||||
refresh()
|
||||
}
|
||||
catch (error) {
|
||||
console.error('删除操作日志失败:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/** 清空操作日志 */
|
||||
async function handleClean() {
|
||||
window.$dialog.warning({
|
||||
title: '确认清空',
|
||||
content: '是否确认清空所有操作日志数据?此操作不可恢复!',
|
||||
positiveText: '确定清空',
|
||||
negativeText: '取消',
|
||||
onPositiveClick: async () => {
|
||||
try {
|
||||
await clearOperLog()
|
||||
window.$message.success('清空成功')
|
||||
refresh()
|
||||
}
|
||||
catch (error) {
|
||||
console.error('清空操作日志失败:', error)
|
||||
}
|
||||
},
|
||||
})
|
||||
}
|
||||
// 创建表格列配置
|
||||
const columns = createTableColumns({
|
||||
onView: handleView,
|
||||
onDelete: handleDelete,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-space vertical>
|
||||
<n-card>
|
||||
<pro-search-form
|
||||
v-bind="proSearchFormProps"
|
||||
:form="searchForm"
|
||||
:columns="searchColumns"
|
||||
/>
|
||||
</n-card>
|
||||
|
||||
<!-- 数据表格 -->
|
||||
<pro-data-table
|
||||
v-bind="tableProps"
|
||||
v-model:checked-row-keys="checkedRowKeys"
|
||||
:columns="columns"
|
||||
row-key="id"
|
||||
:loading="searchLoading"
|
||||
title=" "
|
||||
>
|
||||
<template #toolbar>
|
||||
<n-flex>
|
||||
<n-button
|
||||
type="error"
|
||||
:disabled="checkedRowKeys.length === 0"
|
||||
@click="handleDelete(checkedRowKeys.map(id => Number(id)))"
|
||||
>
|
||||
<template #icon>
|
||||
<icon-park-outline-delete />
|
||||
</template>
|
||||
删除
|
||||
</n-button>
|
||||
<n-button type="warning" @click="handleClean">
|
||||
<template #icon>
|
||||
<icon-park-outline-clear />
|
||||
</template>
|
||||
清空
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
</pro-data-table>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<DetailModal ref="detailModalRef" />
|
||||
</n-space>
|
||||
</template>
|
Loading…
x
Reference in New Issue
Block a user