fix
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user