e2ad1331cc
feat(测试): 新增Playwright和Vitest测试配置 feat(测试): 添加测试覆盖率报告生成功能 feat(测试): 实现前后端测试脚本集成 fix(测试): 修复测试密码不匹配问题 fix(测试): 修正URL等待策略 fix(测试): 调整错误消息选择器 refactor(测试): 重构测试目录结构 refactor(测试): 优化测试用例组织方式 docs: 更新测试报告文档 docs: 添加测试覆盖率报告模板 ci: 添加Docker测试环境配置 ci: 实现测试自动化脚本 chore: 更新依赖版本 chore: 添加测试相关配置文件
236 lines
5.9 KiB
Vue
236 lines
5.9 KiB
Vue
<template>
|
|
<div class="exception-log">
|
|
<el-card>
|
|
<template #header>
|
|
<div class="card-header">
|
|
<div class="search-section">
|
|
<el-input
|
|
v-model="searchKeyword"
|
|
placeholder="搜索操作人或异常信息"
|
|
clearable
|
|
style="width: 300px"
|
|
@clear="handleSearch"
|
|
@keyup.enter="handleSearch"
|
|
>
|
|
<template #prefix>
|
|
<el-icon><Search /></el-icon>
|
|
</template>
|
|
</el-input>
|
|
<el-button
|
|
type="primary"
|
|
@click="handleSearch"
|
|
>
|
|
搜索
|
|
</el-button>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
<el-table
|
|
v-loading="loading"
|
|
:data="dataSource"
|
|
style="width: 100%"
|
|
@sort-change="handleSortChange"
|
|
>
|
|
<el-table-column
|
|
prop="id"
|
|
label="ID"
|
|
sortable="custom"
|
|
width="80"
|
|
/>
|
|
<el-table-column
|
|
prop="username"
|
|
label="操作人"
|
|
sortable="custom"
|
|
width="120"
|
|
/>
|
|
<el-table-column
|
|
prop="operation"
|
|
label="操作模块"
|
|
sortable="custom"
|
|
width="150"
|
|
/>
|
|
<el-table-column
|
|
prop="method"
|
|
label="请求方法"
|
|
sortable="custom"
|
|
width="200"
|
|
:show-overflow-tooltip="true"
|
|
/>
|
|
<el-table-column
|
|
prop="errorMsg"
|
|
label="异常信息"
|
|
:show-overflow-tooltip="true"
|
|
width="250"
|
|
/>
|
|
<el-table-column
|
|
prop="ip"
|
|
label="IP地址"
|
|
sortable="custom"
|
|
width="120"
|
|
/>
|
|
<el-table-column
|
|
prop="createTime"
|
|
label="异常时间"
|
|
sortable="custom"
|
|
width="180"
|
|
/>
|
|
<el-table-column
|
|
label="操作"
|
|
width="120"
|
|
fixed="right"
|
|
>
|
|
<template #default="{ row }">
|
|
<el-button
|
|
type="primary"
|
|
size="small"
|
|
@click="handleViewDetail(row)"
|
|
>
|
|
查看详情
|
|
</el-button>
|
|
</template>
|
|
</el-table-column>
|
|
</el-table>
|
|
<el-pagination
|
|
v-model:current-page="pagination.current"
|
|
v-model:page-size="pagination.pageSize"
|
|
:page-sizes="[10, 20, 50, 100]"
|
|
:total="pagination.total"
|
|
layout="total, sizes, prev, pager, next, jumper"
|
|
style="margin-top: 16px; justify-content: flex-end"
|
|
@current-change="handleTableChange"
|
|
@size-change="handleSizeChange"
|
|
/>
|
|
</el-card>
|
|
|
|
<el-dialog
|
|
v-model="detailVisible"
|
|
title="异常详情"
|
|
width="800px"
|
|
>
|
|
<el-descriptions
|
|
:column="1"
|
|
border
|
|
>
|
|
<el-descriptions-item label="ID">
|
|
{{ currentDetail.id }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="操作人">
|
|
{{ currentDetail.username }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="操作模块">
|
|
{{ currentDetail.operation }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="请求方法">
|
|
{{ currentDetail.method }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="请求参数">
|
|
<pre style="max-height: 200px; overflow: auto;">{{ currentDetail.params }}</pre>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="异常信息">
|
|
<div style="color: #f56c6c; word-break: break-all;">
|
|
{{ currentDetail.errorMsg }}
|
|
</div>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="异常堆栈">
|
|
<pre style="max-height: 300px; overflow: auto; font-size: 12px;">{{ currentDetail.exceptionStack }}</pre>
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="IP地址">
|
|
{{ currentDetail.ip }}
|
|
</el-descriptions-item>
|
|
<el-descriptions-item label="异常时间">
|
|
{{ currentDetail.createTime }}
|
|
</el-descriptions-item>
|
|
</el-descriptions>
|
|
<template #footer>
|
|
<el-button @click="detailVisible = false">
|
|
关闭
|
|
</el-button>
|
|
</template>
|
|
</el-dialog>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, reactive, onMounted } from 'vue'
|
|
import { Search } from '@element-plus/icons-vue'
|
|
import { exceptionLogApi, ExceptionLog } from '@/api/exceptionLog'
|
|
|
|
const loading = ref(false)
|
|
const dataSource = ref<ExceptionLog[]>([])
|
|
const searchKeyword = ref('')
|
|
const pagination = reactive({
|
|
current: 1,
|
|
pageSize: 10,
|
|
total: 0
|
|
})
|
|
|
|
const sortInfo = reactive({
|
|
sort: 'id',
|
|
order: 'asc'
|
|
})
|
|
|
|
const detailVisible = ref(false)
|
|
const currentDetail = ref<ExceptionLog>({})
|
|
|
|
const fetchData = async () => {
|
|
loading.value = true
|
|
try {
|
|
const res = await exceptionLogApi.getPage({
|
|
page: pagination.current - 1,
|
|
size: pagination.pageSize,
|
|
sort: sortInfo.sort,
|
|
order: sortInfo.order,
|
|
keyword: searchKeyword.value || undefined
|
|
})
|
|
dataSource.value = res.content
|
|
pagination.total = res.totalElements
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const handleTableChange = () => {
|
|
fetchData()
|
|
}
|
|
|
|
const handleSizeChange = () => {
|
|
pagination.current = 1
|
|
fetchData()
|
|
}
|
|
|
|
const handleSearch = () => {
|
|
pagination.current = 1
|
|
fetchData()
|
|
}
|
|
|
|
const handleSortChange = ({ prop, order }: any) => {
|
|
sortInfo.sort = prop
|
|
sortInfo.order = order === 'ascending' ? 'asc' : 'desc'
|
|
fetchData()
|
|
}
|
|
|
|
const handleViewDetail = (row: ExceptionLog) => {
|
|
currentDetail.value = { ...row }
|
|
detailVisible.value = true
|
|
}
|
|
|
|
onMounted(() => fetchData())
|
|
</script>
|
|
|
|
<style scoped lang="css">
|
|
.exception-log {
|
|
.card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 16px;
|
|
|
|
.search-section {
|
|
display: flex;
|
|
gap: 8px;
|
|
align-items: center;
|
|
}
|
|
}
|
|
}
|
|
</style>
|