This commit is contained in:
zxr
2026-03-21 17:19:33 +08:00
parent 87741c59ff
commit 6ac9550133

View File

@@ -1,8 +1,7 @@
<template> <template>
<div class="container"> <div class="container">
<Breadcrumb :items="['menu.ops.systemSettings', 'menu.ops.systemSettings.systemLogs']" /> <Breadcrumb :items="['menu.ops.systemSettings', 'menu.ops.systemSettings.systemLogs']" />
<!-- 使用 SearchTable 公共组件 -->
<SearchTable <SearchTable
:form-model="formModel" :form-model="formModel"
:form-items="formItems" :form-items="formItems"
@@ -23,24 +22,57 @@
@refresh="handleRefresh" @refresh="handleRefresh"
@download="handleDownload" @download="handleDownload"
> >
<!-- 表格自定义列日志级别 -->
<template #level="{ record }"> <template #level="{ record }">
<a-tag :color="getLevelColor(record.level)"> <a-tag :color="getLevelColor(record.level)">
{{ record.level }} {{ record.level }}
</a-tag> </a-tag>
</template> </template>
<!-- 表格自定义列序号 --> <template #index="{ rowIndex }">
<template #index="{ rowIndex }">
{{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }} {{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }}
</template> </template>
<!-- 表格自定义列操作 -->
<template #operations="{ record }"> <template #operations="{ record }">
<a-button type="text" size="small" @click="handleView(record)"> <a-button type="text" size="small" @click="handleView(record)">
查看 查看
</a-button> </a-button>
</template> </template>
</SearchTable> </SearchTable>
<a-drawer
v-model:visible="detailVisible"
:width="480"
placement="right"
:title="detailRecord ? `日志详情 #${detailRecord.id}` : '日志详情'"
:footer="false"
unmount-on-close
>
<template v-if="detailRecord">
<a-descriptions :column="1" size="large" bordered>
<a-descriptions-item label="日志级别">
<a-tag :color="getLevelColor(detailRecord.level)">
{{ detailRecord.level }}
</a-tag>
</a-descriptions-item>
<a-descriptions-item label="模块">
{{ detailRecord.module }}
</a-descriptions-item>
<a-descriptions-item label="操作人">
{{ detailRecord.operator }}
</a-descriptions-item>
<a-descriptions-item label="IP 地址">
{{ detailRecord.ip }}
</a-descriptions-item>
<a-descriptions-item label="操作时间">
{{ detailRecord.createdAt }}
</a-descriptions-item>
<a-descriptions-item label="请求 ID">
{{ detailRecord.requestId }}
</a-descriptions-item>
<a-descriptions-item label="日志内容">
<div class="detail-content">{{ detailRecord.content }}</div>
</a-descriptions-item>
</a-descriptions>
</template>
</a-drawer>
</div> </div>
</template> </template>
@@ -50,7 +82,6 @@ import { Message } from '@arco-design/web-vue'
import type { TableColumnData } from '@arco-design/web-vue/es/table/interface' import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'
import type { FormItem } from '@/components/search-form/types' import type { FormItem } from '@/components/search-form/types'
// 定义表格数据类型
interface LogRecord { interface LogRecord {
id: number id: number
level: string level: string
@@ -59,32 +90,42 @@ interface LogRecord {
operator: string operator: string
ip: string ip: string
createdAt: string createdAt: string
/** 用于时间范围筛选(毫秒时间戳) */
timestamp: number
requestId: string
} }
// 模拟数据生成
const generateMockData = (count: number): LogRecord[] => { const generateMockData = (count: number): LogRecord[] => {
const levels = ['INFO', 'WARN', 'ERROR', 'DEBUG'] const levels = ['INFO', 'WARN', 'ERROR', 'DEBUG']
const modules = ['用户管理', '权限管理', '系统配置', '数据备份', '登录认证'] const modules = ['用户管理', '权限管理', '系统配置', '数据备份', '登录认证']
const operators = ['管理员', '张三', '李四', '系统', '定时任务'] const operators = ['管理员', '张三', '李四', '系统', '定时任务']
return Array.from({ length: count }, (_, i) => ({ return Array.from({ length: count }, (_, i) => {
id: i + 1, const timestamp = Date.now() - i * 3600000
level: levels[i % levels.length], return {
module: modules[i % modules.length], id: i + 1,
content: `日志内容描述 ${i + 1}`, level: levels[i % levels.length],
operator: operators[i % operators.length], module: modules[i % modules.length],
ip: `192.168.${Math.floor(i / 255)}.${i % 255}`, content: `日志内容描述 ${i + 1}:系统执行例行检查与状态同步。`,
createdAt: new Date(Date.now() - i * 3600000).toLocaleString('zh-CN'), operator: operators[i % operators.length],
})) ip: `192.168.${Math.floor(i / 255) % 256}.${i % 256}`,
createdAt: new Date(timestamp).toLocaleString('zh-CN'),
timestamp,
requestId: `req-${10000 + i}`,
}
})
} }
// 状态管理
const loading = ref(false) const loading = ref(false)
const tableData = ref<LogRecord[]>([]) const tableData = ref<LogRecord[]>([])
const allFilteredData = ref<LogRecord[]>([])
const formModel = ref({ const formModel = ref({
level: '', level: '',
module: '', module: '',
operator: '', operator: '',
keyword: '',
dateRange: [] as unknown[],
}) })
const pagination = reactive({ const pagination = reactive({
@@ -93,7 +134,9 @@ const pagination = reactive({
total: 0, total: 0,
}) })
// 表单项配置 const detailVisible = ref(false)
const detailRecord = ref<LogRecord | null>(null)
const formItems = computed<FormItem[]>(() => [ const formItems = computed<FormItem[]>(() => [
{ {
field: 'level', field: 'level',
@@ -126,9 +169,21 @@ const formItems = computed<FormItem[]>(() => [
type: 'input', type: 'input',
placeholder: '请输入操作人', placeholder: '请输入操作人',
}, },
{
field: 'keyword',
label: '关键词',
type: 'input',
placeholder: '搜索日志内容',
},
{
field: 'dateRange',
label: '时间范围',
type: 'dateRange',
placeholder: '选择时间范围',
span: 16,
},
]) ])
// 表格列配置
const columns = computed<TableColumnData[]>(() => [ const columns = computed<TableColumnData[]>(() => [
{ {
title: '序号', title: '序号',
@@ -177,7 +232,6 @@ const columns = computed<TableColumnData[]>(() => [
}, },
]) ])
// 获取日志级别颜色
const getLevelColor = (level: string) => { const getLevelColor = (level: string) => {
const colorMap: Record<string, string> = { const colorMap: Record<string, string> = {
INFO: 'arcoblue', INFO: 'arcoblue',
@@ -188,38 +242,54 @@ const getLevelColor = (level: string) => {
return colorMap[level] || 'gray' return colorMap[level] || 'gray'
} }
// 模拟异步获取数据 function applyFilters(source: LogRecord[]): LogRecord[] {
const fetchData = async () => { let data = source
loading.value = true const f = formModel.value
// 模拟网络延迟 if (f.level) {
await new Promise(resolve => setTimeout(resolve, 500)) data = data.filter(item => item.level === f.level)
let data = generateMockData(100)
// 根据搜索条件过滤
if (formModel.value.level) {
data = data.filter(item => item.level === formModel.value.level)
} }
if (formModel.value.module) { if (f.module) {
data = data.filter(item => item.module === formModel.value.module) data = data.filter(item => item.module === f.module)
} }
if (formModel.value.operator) { if (f.operator) {
data = data.filter(item => item.operator.includes(formModel.value.operator)) data = data.filter(item => item.operator.includes(f.operator))
} }
if (f.keyword?.trim()) {
// 更新分页 const kw = f.keyword.trim()
pagination.total = data.length data = data.filter(item => item.content.includes(kw))
}
// 分页截取 if (f.dateRange && f.dateRange.length === 2) {
const [start, end] = f.dateRange
const startMs = new Date(start as string | Date).getTime()
const endMs = new Date(end as string | Date).getTime()
if (!Number.isNaN(startMs) && !Number.isNaN(endMs)) {
data = data.filter(item => item.timestamp >= startMs && item.timestamp <= endMs)
}
}
return data
}
function slicePage(data: LogRecord[]) {
const start = (pagination.current - 1) * pagination.pageSize const start = (pagination.current - 1) * pagination.pageSize
const end = start + pagination.pageSize const end = start + pagination.pageSize
tableData.value = data.slice(start, end) tableData.value = data.slice(start, end)
}
const fetchData = async () => {
loading.value = true
await new Promise(resolve => setTimeout(resolve, 400))
const base = generateMockData(100)
const filtered = applyFilters(base)
allFilteredData.value = filtered
pagination.total = filtered.length
slicePage(filtered)
loading.value = false loading.value = false
} }
// 事件处理
const handleSearch = () => { const handleSearch = () => {
pagination.current = 1 pagination.current = 1
fetchData() fetchData()
@@ -230,6 +300,8 @@ const handleReset = () => {
level: '', level: '',
module: '', module: '',
operator: '', operator: '',
keyword: '',
dateRange: [],
} }
pagination.current = 1 pagination.current = 1
fetchData() fetchData()
@@ -237,7 +309,7 @@ const handleReset = () => {
const handlePageChange = (current: number) => { const handlePageChange = (current: number) => {
pagination.current = current pagination.current = current
fetchData() slicePage(allFilteredData.value)
} }
const handleRefresh = () => { const handleRefresh = () => {
@@ -250,10 +322,10 @@ const handleDownload = () => {
} }
const handleView = (record: LogRecord) => { const handleView = (record: LogRecord) => {
Message.info(`查看日志详情:${record.id}`) detailRecord.value = record
detailVisible.value = true
} }
// 初始化加载数据
fetchData() fetchData()
</script> </script>
@@ -267,4 +339,11 @@ export default {
.container { .container {
padding: 0 20px 20px 20px; padding: 0 20px 20px 20px;
} }
.detail-content {
white-space: pre-wrap;
word-break: break-word;
line-height: 1.6;
color: var(--color-text-1);
}
</style> </style>