feat(log): 新增系统日志管理页面
- 实现日志列表展示与分页功能- 添加日志类型、用户名、操作模块等多条件搜索 - 支持查看日志详情信息 - 提供单条及批量删除日志功能 - 实现过期日志清理功能 - 添加日志导出功能入口 - 集成Element Plus组件库实现UI界面 - 使用Vue 3 Composition API编写组件逻辑
Showing
1 changed file
with
643 additions
and
0 deletions
frontend/src/views/sys/log/index.vue
0 → 100644
| 1 | +<template> | ||
| 2 | + <div class="log-management"> | ||
| 3 | + <!-- 页面标题 --> | ||
| 4 | + <div class="page-header"> | ||
| 5 | + <h2>日志管理</h2> | ||
| 6 | + <p>系统操作日志查询与管理</p> | ||
| 7 | + </div> | ||
| 8 | + | ||
| 9 | + <!-- 搜索表单 --> | ||
| 10 | + <el-card class="search-card" shadow="never"> | ||
| 11 | + <el-form :model="searchForm" :inline="true" class="search-form"> | ||
| 12 | + <el-form-item label="日志类型"> | ||
| 13 | + <el-select v-model="searchForm.logType" placeholder="请选择日志类型" clearable style="width: 200px"> | ||
| 14 | + <el-option label="操作日志" value="0" /> | ||
| 15 | + <el-option label="异常日志" value="1" /> | ||
| 16 | + <el-option label="登录日志" value="2" /> | ||
| 17 | + </el-select> | ||
| 18 | + </el-form-item> | ||
| 19 | + | ||
| 20 | + <el-form-item label="用户名"> | ||
| 21 | + <el-input | ||
| 22 | + v-model="searchForm.username" | ||
| 23 | + placeholder="请输入用户名" | ||
| 24 | + clearable | ||
| 25 | + style="width: 200px" | ||
| 26 | + /> | ||
| 27 | + </el-form-item> | ||
| 28 | + | ||
| 29 | + <el-form-item label="操作模块"> | ||
| 30 | + <el-input | ||
| 31 | + v-model="searchForm.module" | ||
| 32 | + placeholder="请输入操作模块" | ||
| 33 | + clearable | ||
| 34 | + style="width: 200px" | ||
| 35 | + /> | ||
| 36 | + </el-form-item> | ||
| 37 | + | ||
| 38 | + <el-form-item label="操作结果"> | ||
| 39 | + <el-select v-model="searchForm.status" placeholder="请选择操作结果" clearable style="width: 200px"> | ||
| 40 | + <el-option label="成功" :value="1" /> | ||
| 41 | + <el-option label="失败" :value="0" /> | ||
| 42 | + </el-select> | ||
| 43 | + </el-form-item> | ||
| 44 | + | ||
| 45 | + <el-form-item label="时间范围"> | ||
| 46 | + <el-date-picker | ||
| 47 | + v-model="dateRange" | ||
| 48 | + type="datetimerange" | ||
| 49 | + range-separator="至" | ||
| 50 | + start-placeholder="开始时间" | ||
| 51 | + end-placeholder="结束时间" | ||
| 52 | + format="YYYY-MM-DD HH:mm:ss" | ||
| 53 | + value-format="YYYY-MM-DD HH:mm:ss" | ||
| 54 | + @change="handleDateRangeChange" | ||
| 55 | + style="width: 350px" | ||
| 56 | + /> | ||
| 57 | + </el-form-item> | ||
| 58 | + | ||
| 59 | + <el-form-item> | ||
| 60 | + <el-button type="primary" @click="handleSearch" :loading="loading"> | ||
| 61 | + <el-icon><Search /></el-icon> | ||
| 62 | + 搜索 | ||
| 63 | + </el-button> | ||
| 64 | + <el-button @click="handleReset"> | ||
| 65 | + <el-icon><Refresh /></el-icon> | ||
| 66 | + 重置 | ||
| 67 | + </el-button> | ||
| 68 | + </el-form-item> | ||
| 69 | + </el-form> | ||
| 70 | + </el-card> | ||
| 71 | + | ||
| 72 | + <!-- 操作按钮 --> | ||
| 73 | + <el-card class="toolbar-card" shadow="never"> | ||
| 74 | + <div class="toolbar"> | ||
| 75 | + <div class="toolbar-left"> | ||
| 76 | + <el-button | ||
| 77 | + type="danger" | ||
| 78 | + :disabled="selectedLogs.length === 0" | ||
| 79 | + @click="handleBatchDelete" | ||
| 80 | + > | ||
| 81 | + <el-icon><Delete /></el-icon> | ||
| 82 | + 批量删除 | ||
| 83 | + </el-button> | ||
| 84 | + <el-button type="warning" @click="handleCleanExpired"> | ||
| 85 | + <el-icon><Delete /></el-icon> | ||
| 86 | + 清理过期日志 | ||
| 87 | + </el-button> | ||
| 88 | + <el-button type="info" @click="handleExport"> | ||
| 89 | + <el-icon><Download /></el-icon> | ||
| 90 | + 导出日志 | ||
| 91 | + </el-button> | ||
| 92 | + </div> | ||
| 93 | + <div class="toolbar-right"> | ||
| 94 | + <el-button @click="handleRefresh" :loading="loading"> | ||
| 95 | + <el-icon><Refresh /></el-icon> | ||
| 96 | + 刷新 | ||
| 97 | + </el-button> | ||
| 98 | + </div> | ||
| 99 | + </div> | ||
| 100 | + </el-card> | ||
| 101 | + | ||
| 102 | + <!-- 日志列表 --> | ||
| 103 | + <el-card class="table-card" shadow="never"> | ||
| 104 | + | ||
| 105 | + <el-table | ||
| 106 | + v-loading="loading" | ||
| 107 | + :data="logList" | ||
| 108 | + @selection-change="handleSelectionChange" | ||
| 109 | + stripe | ||
| 110 | + border | ||
| 111 | + style="width: 100%" | ||
| 112 | + > | ||
| 113 | + <el-table-column type="selection" width="50" align="center" /> | ||
| 114 | + | ||
| 115 | + <el-table-column prop="logId" label="日志ID" width="80" align="center" /> | ||
| 116 | + | ||
| 117 | + <el-table-column prop="logTypeText" label="日志类型" width="100" align="center"> | ||
| 118 | + <template #default="{ row }"> | ||
| 119 | + <el-tag :type="getLogTypeTagType(row.logType)" size="small"> | ||
| 120 | + {{ row.logTypeText }} | ||
| 121 | + </el-tag> | ||
| 122 | + </template> | ||
| 123 | + </el-table-column> | ||
| 124 | + | ||
| 125 | + <el-table-column prop="username" label="用户名" width="100" align="center" /> | ||
| 126 | + | ||
| 127 | + <el-table-column prop="module" label="操作模块" width="120" align="center" /> | ||
| 128 | + | ||
| 129 | + <el-table-column prop="operation" label="操作内容" min-width="180" show-overflow-tooltip /> | ||
| 130 | + | ||
| 131 | + <el-table-column prop="ip" label="IP地址" width="120" align="center" /> | ||
| 132 | + | ||
| 133 | + <el-table-column prop="logTime" label="操作时间" width="160" align="center"> | ||
| 134 | + <template #default="{ row }"> | ||
| 135 | + {{ formatDate(row.logTime) }} | ||
| 136 | + </template> | ||
| 137 | + </el-table-column> | ||
| 138 | + | ||
| 139 | + <el-table-column prop="statusText" label="操作结果" width="100" align="center"> | ||
| 140 | + <template #default="{ row }"> | ||
| 141 | + <el-tag :type="row.status === 1 ? 'success' : 'danger'" size="small"> | ||
| 142 | + {{ row.statusText }} | ||
| 143 | + </el-tag> | ||
| 144 | + </template> | ||
| 145 | + </el-table-column> | ||
| 146 | + | ||
| 147 | + <el-table-column label="操作" width="140" fixed="right" align="center"> | ||
| 148 | + <template #default="{ row }"> | ||
| 149 | + <el-button | ||
| 150 | + type="primary" | ||
| 151 | + size="small" | ||
| 152 | + @click="handleViewDetail(row)" | ||
| 153 | + link | ||
| 154 | + > | ||
| 155 | + 详情 | ||
| 156 | + </el-button> | ||
| 157 | + <el-button | ||
| 158 | + type="danger" | ||
| 159 | + size="small" | ||
| 160 | + @click="handleDelete(row)" | ||
| 161 | + link | ||
| 162 | + > | ||
| 163 | + 删除 | ||
| 164 | + </el-button> | ||
| 165 | + </template> | ||
| 166 | + </el-table-column> | ||
| 167 | + </el-table> | ||
| 168 | + | ||
| 169 | + <!-- 分页 --> | ||
| 170 | + <div class="pagination-container"> | ||
| 171 | + <el-pagination | ||
| 172 | + v-model:current-page="pagination.pageNum" | ||
| 173 | + v-model:page-size="pagination.pageSize" | ||
| 174 | + :page-sizes="[10, 20, 50, 100]" | ||
| 175 | + :total="pagination.total" | ||
| 176 | + layout="total, sizes, prev, pager, next, jumper" | ||
| 177 | + @size-change="handleSizeChange" | ||
| 178 | + @current-change="handleCurrentChange" | ||
| 179 | + /> | ||
| 180 | + </div> | ||
| 181 | + </el-card> | ||
| 182 | + | ||
| 183 | + <!-- 日志详情对话框 --> | ||
| 184 | + <el-dialog | ||
| 185 | + v-model="detailDialogVisible" | ||
| 186 | + title="日志详情" | ||
| 187 | + width="800px" | ||
| 188 | + :close-on-click-modal="false" | ||
| 189 | + > | ||
| 190 | + <div v-if="logDetail" class="log-detail"> | ||
| 191 | + <el-descriptions :column="2" border> | ||
| 192 | + <el-descriptions-item label="日志ID"> | ||
| 193 | + {{ logDetail.logId }} | ||
| 194 | + </el-descriptions-item> | ||
| 195 | + <el-descriptions-item label="日志类型"> | ||
| 196 | + <el-tag :type="getLogTypeTagType(logDetail.logType)"> | ||
| 197 | + {{ logDetail.logTypeText }} | ||
| 198 | + </el-tag> | ||
| 199 | + </el-descriptions-item> | ||
| 200 | + <el-descriptions-item label="用户名"> | ||
| 201 | + {{ logDetail.username }} | ||
| 202 | + </el-descriptions-item> | ||
| 203 | + <el-descriptions-item label="用户ID"> | ||
| 204 | + {{ logDetail.userId }} | ||
| 205 | + </el-descriptions-item> | ||
| 206 | + <el-descriptions-item label="操作模块"> | ||
| 207 | + {{ logDetail.module }} | ||
| 208 | + </el-descriptions-item> | ||
| 209 | + <el-descriptions-item label="操作内容"> | ||
| 210 | + {{ logDetail.operation }} | ||
| 211 | + </el-descriptions-item> | ||
| 212 | + <el-descriptions-item label="IP地址"> | ||
| 213 | + {{ logDetail.ip }} | ||
| 214 | + </el-descriptions-item> | ||
| 215 | + <el-descriptions-item label="操作时间"> | ||
| 216 | + {{ formatDate(logDetail.logTime) }} | ||
| 217 | + </el-descriptions-item> | ||
| 218 | + <el-descriptions-item label="操作结果"> | ||
| 219 | + <el-tag :type="logDetail.status === 1 ? 'success' : 'danger'"> | ||
| 220 | + {{ logDetail.statusText }} | ||
| 221 | + </el-tag> | ||
| 222 | + </el-descriptions-item> | ||
| 223 | + <el-descriptions-item label="错误信息" v-if="logDetail.errorMsg"> | ||
| 224 | + {{ logDetail.errorMsg }} | ||
| 225 | + </el-descriptions-item> | ||
| 226 | + </el-descriptions> | ||
| 227 | + | ||
| 228 | + <div v-if="logDetail.requestParam" class="request-param"> | ||
| 229 | + <h4>请求参数</h4> | ||
| 230 | + <el-input | ||
| 231 | + v-model="logDetail.requestParam" | ||
| 232 | + type="textarea" | ||
| 233 | + :rows="6" | ||
| 234 | + readonly | ||
| 235 | + placeholder="无请求参数" | ||
| 236 | + /> | ||
| 237 | + </div> | ||
| 238 | + </div> | ||
| 239 | + | ||
| 240 | + <template #footer> | ||
| 241 | + <el-button @click="detailDialogVisible = false">关闭</el-button> | ||
| 242 | + </template> | ||
| 243 | + </el-dialog> | ||
| 244 | + | ||
| 245 | + <!-- 清理过期日志对话框 --> | ||
| 246 | + <el-dialog | ||
| 247 | + v-model="cleanDialogVisible" | ||
| 248 | + title="清理过期日志" | ||
| 249 | + width="400px" | ||
| 250 | + :close-on-click-modal="false" | ||
| 251 | + > | ||
| 252 | + <el-form :model="cleanForm" label-width="100px"> | ||
| 253 | + <el-form-item label="过期天数"> | ||
| 254 | + <el-input-number | ||
| 255 | + v-model="cleanForm.expireDays" | ||
| 256 | + :min="1" | ||
| 257 | + :max="365" | ||
| 258 | + placeholder="请输入过期天数" | ||
| 259 | + style="width: 100%" | ||
| 260 | + /> | ||
| 261 | + </el-form-item> | ||
| 262 | + <el-form-item> | ||
| 263 | + <el-text type="warning"> | ||
| 264 | + 将删除指定天数之前的所有日志记录,此操作不可恢复! | ||
| 265 | + </el-text> | ||
| 266 | + </el-form-item> | ||
| 267 | + </el-form> | ||
| 268 | + | ||
| 269 | + <template #footer> | ||
| 270 | + <el-button @click="cleanDialogVisible = false">取消</el-button> | ||
| 271 | + <el-button type="danger" @click="handleConfirmClean" :loading="cleanLoading"> | ||
| 272 | + 确认清理 | ||
| 273 | + </el-button> | ||
| 274 | + </template> | ||
| 275 | + </el-dialog> | ||
| 276 | + </div> | ||
| 277 | +</template> | ||
| 278 | + | ||
| 279 | +<script setup lang="ts"> | ||
| 280 | +import { ref, reactive, onMounted } from 'vue' | ||
| 281 | +import { ElMessage, ElMessageBox } from 'element-plus' | ||
| 282 | +import { Search, Refresh, Delete, Download } from '@element-plus/icons-vue' | ||
| 283 | +import { logApi, type Log, type LogDetail, type LogSearchParams } from '@/api/log' | ||
| 284 | +import { formatDate } from '@/utils/index' | ||
| 285 | + | ||
| 286 | +// 响应式数据 | ||
| 287 | +const loading = ref(false) | ||
| 288 | +const logList = ref<Log[]>([]) | ||
| 289 | +const selectedLogs = ref<Log[]>([]) | ||
| 290 | +const detailDialogVisible = ref(false) | ||
| 291 | +const logDetail = ref<LogDetail | null>(null) | ||
| 292 | +const cleanDialogVisible = ref(false) | ||
| 293 | +const cleanLoading = ref(false) | ||
| 294 | +const dateRange = ref<[string, string] | null>(null) | ||
| 295 | + | ||
| 296 | +// 搜索表单 | ||
| 297 | +const searchForm = reactive<LogSearchParams>({ | ||
| 298 | + pageNum: 1, | ||
| 299 | + pageSize: 10, | ||
| 300 | + logType: '', | ||
| 301 | + username: '', | ||
| 302 | + module: '', | ||
| 303 | + status: undefined, | ||
| 304 | + startTime: '', | ||
| 305 | + endTime: '' | ||
| 306 | +}) | ||
| 307 | + | ||
| 308 | +// 分页信息 | ||
| 309 | +const pagination = reactive({ | ||
| 310 | + pageNum: 1, | ||
| 311 | + pageSize: 10, | ||
| 312 | + total: 0 | ||
| 313 | +}) | ||
| 314 | + | ||
| 315 | +// 清理表单 | ||
| 316 | +const cleanForm = reactive({ | ||
| 317 | + expireDays: 30 | ||
| 318 | +}) | ||
| 319 | + | ||
| 320 | +// 获取日志类型标签类型 | ||
| 321 | +const getLogTypeTagType = (logType: string) => { | ||
| 322 | + switch (logType) { | ||
| 323 | + case '0': | ||
| 324 | + return 'primary' | ||
| 325 | + case '1': | ||
| 326 | + return 'danger' | ||
| 327 | + case '2': | ||
| 328 | + return 'success' | ||
| 329 | + default: | ||
| 330 | + return 'info' | ||
| 331 | + } | ||
| 332 | +} | ||
| 333 | + | ||
| 334 | + | ||
| 335 | +// 导出日志 | ||
| 336 | +const handleExport = () => { | ||
| 337 | + ElMessage.info('导出功能开发中...') | ||
| 338 | +} | ||
| 339 | + | ||
| 340 | +// 获取日志列表 | ||
| 341 | +const getLogList = async () => { | ||
| 342 | + try { | ||
| 343 | + loading.value = true | ||
| 344 | + const params = { | ||
| 345 | + ...searchForm, | ||
| 346 | + pageNum: pagination.pageNum, | ||
| 347 | + pageSize: pagination.pageSize | ||
| 348 | + } | ||
| 349 | + | ||
| 350 | + const response = await logApi.getLogList(params) | ||
| 351 | + if (response.code === 200) { | ||
| 352 | + logList.value = response.data.records | ||
| 353 | + pagination.total = response.data.total | ||
| 354 | + } else { | ||
| 355 | + ElMessage.error(response.message || '获取日志列表失败') | ||
| 356 | + } | ||
| 357 | + } catch (error) { | ||
| 358 | + console.error('获取日志列表失败:', error) | ||
| 359 | + ElMessage.error('获取日志列表失败') | ||
| 360 | + } finally { | ||
| 361 | + loading.value = false | ||
| 362 | + } | ||
| 363 | +} | ||
| 364 | + | ||
| 365 | +// 搜索 | ||
| 366 | +const handleSearch = () => { | ||
| 367 | + pagination.pageNum = 1 | ||
| 368 | + getLogList() | ||
| 369 | +} | ||
| 370 | + | ||
| 371 | +// 重置 | ||
| 372 | +const handleReset = () => { | ||
| 373 | + Object.assign(searchForm, { | ||
| 374 | + pageNum: 1, | ||
| 375 | + pageSize: 10, | ||
| 376 | + logType: '', | ||
| 377 | + username: '', | ||
| 378 | + module: '', | ||
| 379 | + status: undefined, | ||
| 380 | + startTime: '', | ||
| 381 | + endTime: '' | ||
| 382 | + }) | ||
| 383 | + dateRange.value = null | ||
| 384 | + pagination.pageNum = 1 | ||
| 385 | + getLogList() | ||
| 386 | +} | ||
| 387 | + | ||
| 388 | +// 刷新 | ||
| 389 | +const handleRefresh = () => { | ||
| 390 | + getLogList() | ||
| 391 | +} | ||
| 392 | + | ||
| 393 | +// 时间范围变化 | ||
| 394 | +const handleDateRangeChange = (value: [string, string] | null) => { | ||
| 395 | + if (value) { | ||
| 396 | + searchForm.startTime = value[0] | ||
| 397 | + searchForm.endTime = value[1] | ||
| 398 | + } else { | ||
| 399 | + searchForm.startTime = '' | ||
| 400 | + searchForm.endTime = '' | ||
| 401 | + } | ||
| 402 | +} | ||
| 403 | + | ||
| 404 | +// 分页大小变化 | ||
| 405 | +const handleSizeChange = (size: number) => { | ||
| 406 | + pagination.pageSize = size | ||
| 407 | + pagination.pageNum = 1 | ||
| 408 | + getLogList() | ||
| 409 | +} | ||
| 410 | + | ||
| 411 | +// 当前页变化 | ||
| 412 | +const handleCurrentChange = (page: number) => { | ||
| 413 | + pagination.pageNum = page | ||
| 414 | + getLogList() | ||
| 415 | +} | ||
| 416 | + | ||
| 417 | +// 选择变化 | ||
| 418 | +const handleSelectionChange = (selection: Log[]) => { | ||
| 419 | + selectedLogs.value = selection | ||
| 420 | +} | ||
| 421 | + | ||
| 422 | +// 查看详情 | ||
| 423 | +const handleViewDetail = async (row: Log) => { | ||
| 424 | + try { | ||
| 425 | + const response = await logApi.getLogDetail(row.logId) | ||
| 426 | + if (response.code === 200) { | ||
| 427 | + logDetail.value = response.data | ||
| 428 | + detailDialogVisible.value = true | ||
| 429 | + } else { | ||
| 430 | + ElMessage.error(response.message || '获取日志详情失败') | ||
| 431 | + } | ||
| 432 | + } catch (error) { | ||
| 433 | + console.error('获取日志详情失败:', error) | ||
| 434 | + ElMessage.error('获取日志详情失败') | ||
| 435 | + } | ||
| 436 | +} | ||
| 437 | + | ||
| 438 | +// 删除日志 | ||
| 439 | +const handleDelete = async (row: Log) => { | ||
| 440 | + try { | ||
| 441 | + await ElMessageBox.confirm( | ||
| 442 | + `确定要删除日志ID为 ${row.logId} 的记录吗?`, | ||
| 443 | + '确认删除', | ||
| 444 | + { | ||
| 445 | + confirmButtonText: '确定', | ||
| 446 | + cancelButtonText: '取消', | ||
| 447 | + type: 'warning' | ||
| 448 | + } | ||
| 449 | + ) | ||
| 450 | + | ||
| 451 | + const response = await logApi.deleteLog(row.logId) | ||
| 452 | + if (response.code === 200) { | ||
| 453 | + ElMessage.success('删除成功') | ||
| 454 | + getLogList() | ||
| 455 | + } else { | ||
| 456 | + ElMessage.error(response.message || '删除失败') | ||
| 457 | + } | ||
| 458 | + } catch (error) { | ||
| 459 | + if (error !== 'cancel') { | ||
| 460 | + console.error('删除日志失败:', error) | ||
| 461 | + ElMessage.error('删除失败') | ||
| 462 | + } | ||
| 463 | + } | ||
| 464 | +} | ||
| 465 | + | ||
| 466 | +// 批量删除 | ||
| 467 | +const handleBatchDelete = async () => { | ||
| 468 | + if (selectedLogs.value.length === 0) { | ||
| 469 | + ElMessage.warning('请选择要删除的日志') | ||
| 470 | + return | ||
| 471 | + } | ||
| 472 | + | ||
| 473 | + try { | ||
| 474 | + await ElMessageBox.confirm( | ||
| 475 | + `确定要删除选中的 ${selectedLogs.value.length} 条日志记录吗?`, | ||
| 476 | + '确认批量删除', | ||
| 477 | + { | ||
| 478 | + confirmButtonText: '确定', | ||
| 479 | + cancelButtonText: '取消', | ||
| 480 | + type: 'warning' | ||
| 481 | + } | ||
| 482 | + ) | ||
| 483 | + | ||
| 484 | + const logIds = selectedLogs.value.map(log => log.logId) | ||
| 485 | + const response = await logApi.deleteLogs(logIds) | ||
| 486 | + if (response.code === 200) { | ||
| 487 | + ElMessage.success('批量删除成功') | ||
| 488 | + getLogList() | ||
| 489 | + } else { | ||
| 490 | + ElMessage.error(response.message || '批量删除失败') | ||
| 491 | + } | ||
| 492 | + } catch (error) { | ||
| 493 | + if (error !== 'cancel') { | ||
| 494 | + console.error('批量删除日志失败:', error) | ||
| 495 | + ElMessage.error('批量删除失败') | ||
| 496 | + } | ||
| 497 | + } | ||
| 498 | +} | ||
| 499 | + | ||
| 500 | +// 清理过期日志 | ||
| 501 | +const handleCleanExpired = () => { | ||
| 502 | + cleanDialogVisible.value = true | ||
| 503 | +} | ||
| 504 | + | ||
| 505 | +// 确认清理 | ||
| 506 | +const handleConfirmClean = async () => { | ||
| 507 | + if (!cleanForm.expireDays || cleanForm.expireDays <= 0) { | ||
| 508 | + ElMessage.warning('请输入有效的过期天数') | ||
| 509 | + return | ||
| 510 | + } | ||
| 511 | + | ||
| 512 | + try { | ||
| 513 | + await ElMessageBox.confirm( | ||
| 514 | + `确定要清理 ${cleanForm.expireDays} 天前的所有日志记录吗?此操作不可恢复!`, | ||
| 515 | + '确认清理', | ||
| 516 | + { | ||
| 517 | + confirmButtonText: '确定', | ||
| 518 | + cancelButtonText: '取消', | ||
| 519 | + type: 'warning' | ||
| 520 | + } | ||
| 521 | + ) | ||
| 522 | + | ||
| 523 | + cleanLoading.value = true | ||
| 524 | + const response = await logApi.cleanExpiredLogs(cleanForm.expireDays) | ||
| 525 | + if (response.code === 200) { | ||
| 526 | + ElMessage.success(response.data) | ||
| 527 | + cleanDialogVisible.value = false | ||
| 528 | + getLogList() | ||
| 529 | + } else { | ||
| 530 | + ElMessage.error(response.message || '清理失败') | ||
| 531 | + } | ||
| 532 | + } catch (error) { | ||
| 533 | + if (error !== 'cancel') { | ||
| 534 | + console.error('清理过期日志失败:', error) | ||
| 535 | + ElMessage.error('清理失败') | ||
| 536 | + } | ||
| 537 | + } finally { | ||
| 538 | + cleanLoading.value = false | ||
| 539 | + } | ||
| 540 | +} | ||
| 541 | + | ||
| 542 | +// 组件挂载时获取数据 | ||
| 543 | +onMounted(() => { | ||
| 544 | + getLogList() | ||
| 545 | +}) | ||
| 546 | +</script> | ||
| 547 | + | ||
| 548 | +<style scoped> | ||
| 549 | +.log-management { | ||
| 550 | + padding: 20px; | ||
| 551 | +} | ||
| 552 | + | ||
| 553 | +.page-header { | ||
| 554 | + margin-bottom: 20px; | ||
| 555 | +} | ||
| 556 | + | ||
| 557 | +.page-header h2 { | ||
| 558 | + margin: 0 0 8px 0; | ||
| 559 | + color: #303133; | ||
| 560 | + font-size: 20px; | ||
| 561 | + font-weight: 600; | ||
| 562 | +} | ||
| 563 | + | ||
| 564 | +.page-header p { | ||
| 565 | + margin: 0; | ||
| 566 | + color: #909399; | ||
| 567 | + font-size: 14px; | ||
| 568 | +} | ||
| 569 | + | ||
| 570 | +.search-card { | ||
| 571 | + margin-bottom: 16px; | ||
| 572 | +} | ||
| 573 | + | ||
| 574 | +.search-form { | ||
| 575 | + margin-bottom: 0; | ||
| 576 | +} | ||
| 577 | + | ||
| 578 | +.toolbar-card { | ||
| 579 | + margin-bottom: 16px; | ||
| 580 | +} | ||
| 581 | + | ||
| 582 | +.toolbar { | ||
| 583 | + display: flex; | ||
| 584 | + justify-content: space-between; | ||
| 585 | + align-items: center; | ||
| 586 | +} | ||
| 587 | + | ||
| 588 | +.toolbar-left, | ||
| 589 | +.toolbar-right { | ||
| 590 | + display: flex; | ||
| 591 | + gap: 8px; | ||
| 592 | +} | ||
| 593 | + | ||
| 594 | +.table-card { | ||
| 595 | + margin-bottom: 16px; | ||
| 596 | +} | ||
| 597 | + | ||
| 598 | +.pagination-container { | ||
| 599 | + display: flex; | ||
| 600 | + justify-content: center; | ||
| 601 | + margin-top: 20px; | ||
| 602 | +} | ||
| 603 | + | ||
| 604 | +.log-detail { | ||
| 605 | + max-height: 600px; | ||
| 606 | + overflow-y: auto; | ||
| 607 | +} | ||
| 608 | + | ||
| 609 | +.request-param { | ||
| 610 | + margin-top: 20px; | ||
| 611 | +} | ||
| 612 | + | ||
| 613 | +.request-param h4 { | ||
| 614 | + margin: 0 0 10px 0; | ||
| 615 | + color: #303133; | ||
| 616 | + font-size: 14px; | ||
| 617 | + font-weight: 600; | ||
| 618 | +} | ||
| 619 | + | ||
| 620 | +/* 表格优化 */ | ||
| 621 | +.el-table { | ||
| 622 | + font-size: 14px; | ||
| 623 | +} | ||
| 624 | + | ||
| 625 | +.el-table .cell { | ||
| 626 | + padding: 8px 12px; | ||
| 627 | + line-height: 1.5; | ||
| 628 | +} | ||
| 629 | + | ||
| 630 | +.el-table th.el-table__cell { | ||
| 631 | + background: #fafafa; | ||
| 632 | + color: #606266; | ||
| 633 | + font-weight: 600; | ||
| 634 | +} | ||
| 635 | + | ||
| 636 | +.el-table td.el-table__cell { | ||
| 637 | + padding: 12px 0; | ||
| 638 | +} | ||
| 639 | + | ||
| 640 | +.el-table .el-button + .el-button { | ||
| 641 | + margin-left: 8px; | ||
| 642 | +} | ||
| 643 | +</style> |
-
Please register or login to post a comment