Browse Source

2025-11-04

pc接口日志管理
master
fengyuan_yang 2 months ago
parent
commit
d5f6d907e1
  1. 15
      src/api/sys/interface-log.js
  2. 698
      src/views/modules/sys/interface/interfaceLog.vue

15
src/api/sys/interface-log.js

@ -0,0 +1,15 @@
import { createAPI } from "@/utils/httpRequest.js";
// ===================================== 接口日志管理 =====================================
// 查询接口日志列表
export const searchApiLogs = data => createAPI(`/sys/interfaceLog/list`,'post',data)
// 查询接口参数(主表+明细)
export const getApiLogParams = data => createAPI(`/sys/interfaceLog/getParams`,'post',data)
// 删除接口日志
export const deleteApiLogs = data => createAPI(`/sys/interfaceLog/delete`,'post',data)
// 重试接口
export const retryApiLogs = data => createAPI(`/sys/interfaceLog/retry`,'post',data)

698
src/views/modules/sys/interface/interfaceLog.vue

@ -0,0 +1,698 @@
<template>
<div class="mod-config">
<!-- 条件查询 -->
<el-card :class="['search-card', { 'collapsed': !searchExpanded }]" shadow="hover">
<div slot="header" class="search-header">
<div class="header-left">
<i class="el-icon-search"></i>
<span class="header-title">接口日志查询</span>
</div>
<div class="header-right">
<el-button
type="text"
size="small"
@click="toggleSearchExpand"
class="collapse-btn">
<i :class="searchExpanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
{{ searchExpanded ? '收起' : '展开' }}
</el-button>
</div>
</div>
<el-form
:model="searchForm"
label-width="110px"
class="search-form"
@keyup.enter.native="getDataList">
<!-- 所有查询条件 - 可展开/收起 -->
<template v-if="searchExpanded">
<!-- 第一行 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="接口名称">
<el-input
v-model="searchForm.interfaceName"
placeholder="请输入接口名称"
clearable
prefix-icon="el-icon-document">
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="请求ID">
<el-input
v-model="searchForm.requestId"
placeholder="请输入请求ID"
clearable
prefix-icon="el-icon-key">
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="关联单据号">
<el-input
v-model="searchForm.reDocumentNo"
placeholder="请输入关联单据号"
clearable>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="关联单据类型">
<el-input
v-model="searchForm.reDocumentType"
placeholder="请输入关联单据类型"
clearable>
</el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 第二行 -->
<el-row :gutter="20">
<el-col :span="6">
<el-form-item label="状态码">
<el-input
v-model="searchForm.statusCode"
placeholder="请输入状态码"
clearable>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="来源系统">
<el-input
v-model="searchForm.sourceSystem"
placeholder="请输入来源系统"
clearable>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="目标系统">
<el-input
v-model="searchForm.targetSystem"
placeholder="请输入目标系统"
clearable>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item label="是否需要重试">
<el-select v-model="searchForm.needRetryFlag" placeholder="请选择" clearable>
<el-option label="是" :value="1"></el-option>
<el-option label="否" :value="0"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 第三行 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="创建时间">
<el-date-picker
v-model="searchForm.dateRange"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="yyyy-MM-dd"
style="width: 100%">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
</template>
<!-- 操作按钮区域 -->
<div class="search-actions">
<div class="action-left">
<el-button type="primary" @click="getDataList" icon="el-icon-search">查询</el-button>
<el-button @click="resetSearch" icon="el-icon-refresh-left">重置</el-button>
</div>
</div>
</el-form>
</el-card>
<!-- 表格操作按钮 -->
<div class="table-actions">
<el-button
v-if="dataListSelections.length > 0"
type="danger"
@click="deleteHandle()"
icon="el-icon-delete"
size="small">
批量删除
</el-button>
<el-button
v-if="dataListSelections.length > 0"
type="success"
@click="retryHandle()"
icon="el-icon-refresh"
size="small">
批量重试
</el-button>
</div>
<!-- 数据表格 -->
<el-table
:height="tableHeight"
:data="dataList"
border
v-loading="dataListLoading"
@selection-change="selectionChangeHandle"
style="width: 100%;">
<el-table-column
type="selection"
header-align="center"
align="center"
width="50">
</el-table-column>
<el-table-column
prop="id"
header-align="center"
align="center"
label="ID"
width="80">
</el-table-column>
<el-table-column
prop="requestId"
header-align="center"
align="center"
label="请求ID"
width="200">
</el-table-column>
<el-table-column
prop="interfaceName"
header-align="center"
align="center"
label="接口名称"
min-width="150">
</el-table-column>
<el-table-column
prop="reDocumentNo"
header-align="center"
align="center"
label="关联单据号"
min-width="120">
</el-table-column>
<el-table-column
prop="reDocumentType"
header-align="center"
align="center"
label="关联单据类型"
min-width="120">
</el-table-column>
<el-table-column
prop="statusCode"
header-align="center"
align="center"
label="状态码"
width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.statusCode === '200' ? 'success' : 'danger'">
{{ scope.row.statusCode }}
</el-tag>
</template>
</el-table-column>
<el-table-column
prop="message"
header-align="center"
align="center"
label="消息"
min-width="150"
show-overflow-tooltip>
</el-table-column>
<el-table-column
prop="sourceSystem"
header-align="center"
align="center"
label="来源系统"
width="100">
</el-table-column>
<el-table-column
prop="targetSystem"
header-align="center"
align="center"
label="目标系统"
width="100">
</el-table-column>
<el-table-column
prop="retryCount"
header-align="center"
align="center"
label="重试次数"
width="90">
</el-table-column>
<el-table-column
prop="createdDate"
header-align="center"
align="center"
label="创建时间"
width="155">
</el-table-column>
<el-table-column
fixed="right"
header-align="center"
align="center"
width="100"
label="操作">
<template slot-scope="scope">
<el-button type="text" size="small" @click="viewParamsHandle(scope.row)">
接口参数
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
@size-change="sizeChangeHandle"
@current-change="currentChangeHandle"
:current-page="pageIndex"
:page-sizes="[10, 20, 50, 100, 200]"
:page-size="pageSize"
:total="totalPage"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 0px;">
</el-pagination>
<!-- 接口参数对话框 -->
<el-dialog
title="接口参数详情"
:visible.sync="paramsDialogVisible"
width="60%"
:close-on-click-modal="false">
<div class="json-viewer">
<pre>{{ formattedParams }}</pre>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="paramsDialogVisible = false"> </el-button>
<el-button type="primary" @click="copyParams">复制参数</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { searchApiLogs, getApiLogParams, deleteApiLogs, retryApiLogs } from '@/api/sys/interface-log'
export default {
data() {
return {
searchExpanded: false,
searchForm: {
interfaceName: '',
requestId: '',
reDocumentNo: '',
reDocumentType: '',
statusCode: '',
sourceSystem: '',
targetSystem: '',
needRetryFlag: null,
dateRange: []
},
dataList: [],
pageIndex: 1,
pageSize: 20,
totalPage: 0,
dataListLoading: false,
dataListSelections: [],
paramsDialogVisible: false,
currentParams: null,
tableHeight: 200
}
},
computed: {
formattedParams() {
if (!this.currentParams) return ''
return JSON.stringify(this.currentParams, null, 2)
}
},
mounted() {
this.$nextTick(() => {
this.tableHeight = window.innerHeight - 280
})
this.getDataList()
},
methods: {
// /
toggleSearchExpand() {
this.searchExpanded = !this.searchExpanded
},
//
getDataList() {
this.dataListLoading = true
const params = {
page: this.pageIndex,
limit: this.pageSize,
...this.searchForm
}
//
if (this.searchForm.dateRange && this.searchForm.dateRange.length === 2) {
params.startDate = this.searchForm.dateRange[0]
params.endDate = this.searchForm.dateRange[1]
}
delete params.dateRange
searchApiLogs(params).then(({ data }) => {
if (data && data.code === 0) {
this.dataList = data.page.list
this.totalPage = data.page.totalCount
} else {
this.dataList = []
this.totalPage = 0
}
this.dataListLoading = false
}).catch(() => {
this.dataListLoading = false
})
},
//
resetSearch() {
this.searchForm = {
interfaceName: '',
requestId: '',
reDocumentNo: '',
reDocumentType: '',
statusCode: '',
sourceSystem: '',
targetSystem: '',
needRetryFlag: null,
dateRange: []
}
this.pageIndex = 1
this.getDataList()
},
//
sizeChangeHandle(val) {
this.pageSize = val
this.pageIndex = 1
this.getDataList()
},
//
currentChangeHandle(val) {
this.pageIndex = val
this.getDataList()
},
//
selectionChangeHandle(val) {
this.dataListSelections = val
},
//
viewParamsHandle(row) {
const params = {
site: row.site,
buNo: row.buNo,
requestId: row.requestId,
requestGroupId: row.requestGroupId
}
getApiLogParams(params).then(({ data }) => {
if (data && data.code === 0) {
this.currentParams = data.params
this.paramsDialogVisible = true
} else {
this.$message.error(data.msg || '获取接口参数失败')
}
})
},
//
copyParams() {
const textarea = document.createElement('textarea')
textarea.value = this.formattedParams
document.body.appendChild(textarea)
textarea.select()
document.execCommand('copy')
document.body.removeChild(textarea)
this.$message.success('复制成功')
},
//
deleteHandle() {
const ids = this.dataListSelections.map(item => item.id)
this.$confirm('确定要删除选中的记录吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
deleteApiLogs({ ids }).then(({ data }) => {
if (data && data.code === 0) {
this.$message.success('删除成功')
this.getDataList()
} else {
this.$message.error(data.msg || '删除失败')
}
})
})
},
//
retryHandle() {
const ids = this.dataListSelections.map(item => item.id)
this.$confirm('确定要重试选中的接口吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
retryApiLogs({ ids }).then(({ data }) => {
if (data && data.code === 0) {
this.$message.success('重试成功')
this.getDataList()
} else {
this.$message.error(data.msg || '重试失败')
}
})
})
}
}
}
</script>
<style scoped>
/* 搜索卡片样式 */
.search-card {
margin-bottom: 16px;
border-radius: 8px;
overflow: hidden;
transition: all 0.3s ease;
}
.search-card:hover {
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
}
.search-card /deep/ .el-card__header {
padding: 5px 20px;
background: linear-gradient(135deg, #9ac3d0 20%, #b6c7dd 80%);
border-bottom: none;
}
.search-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.header-left {
display: flex;
align-items: center;
color: #fff;
}
.header-left i {
font-size: 16px;
margin-right: 8px;
}
.header-title {
font-size: 14px;
font-weight: 600;
letter-spacing: 0.5px;
}
.header-right {
color: #fff;
}
.collapse-btn {
color: #fff;
font-weight: 500;
transition: all 0.3s ease;
}
.collapse-btn:hover {
color: #f0f0f0;
transform: translateY(-1px);
}
.collapse-btn i {
transition: transform 0.3s ease;
}
/* 搜索表单样式 */
.search-form {
padding: 10px 0;
min-height: 0;
}
/* 卡片主体样式 */
.search-card /deep/ .el-card__body {
padding: 10px;
transition: all 0.3s ease;
}
/* 收起时的样式 */
.search-card.collapsed /deep/ .el-card__body {
padding: 10px 20px;
}
.search-form /deep/ .el-form-item {
margin-bottom: 18px;
}
.search-form /deep/ .el-form-item__label {
font-weight: 500;
color: #606266;
padding-bottom: 6px;
}
.search-form /deep/ .el-input__inner,
.search-form /deep/ .el-textarea__inner {
border-radius: 6px;
border: 1px solid #DCDFE6;
transition: all 0.3s ease;
}
.search-form /deep/ .el-input__inner:focus,
.search-form /deep/ .el-textarea__inner:focus {
border-color: #667eea;
box-shadow: 0 0 0 2px rgba(102, 126, 234, 0.1);
}
.search-form /deep/ .el-select {
width: 100%;
}
.search-form /deep/ .el-date-editor.el-input {
width: 100%;
}
/* 操作按钮区域 */
.search-actions {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0 2px 0;
}
/* 展开时显示上边框 */
.search-card:not(.collapsed) .search-actions {
border-top: 1px solid #f0f0f0;
margin-top: 8px;
}
/* 收起时不显示上边框和上边距 */
.search-card.collapsed .search-actions {
border-top: none;
margin-top: 0;
padding-top: 0;
}
.action-left,
.action-right {
display: flex;
gap: 8px;
}
.search-actions .el-button {
border-radius: 4px;
padding: 5px 10px;
font-size: 12px;
font-weight: 500;
transition: all 0.3s ease;
}
.search-actions .el-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.search-actions .el-button--primary {
background: #60aeff;
border-color: #60aeff;
}
.search-actions .el-button--primary:hover {
background: #7dbdff;
border-color: #7dbdff;
}
/* 表格操作按钮区域 */
.table-actions {
margin-bottom: 8px;
margin-top: 8px;
}
.table-actions .el-button {
border-radius: 4px;
font-weight: 500;
transition: all 0.3s ease;
}
.table-actions .el-button:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
/* 响应式设计 */
@media (max-width: 1200px) {
.search-actions {
flex-direction: column;
gap: 10px;
}
.action-left,
.action-right {
width: 100%;
justify-content: center;
}
}
/* 表格样式 */
.el-table /deep/ .cell {
height: auto;
line-height: 1.5;
}
/* JSON查看器样式 */
.json-viewer {
max-height: 500px;
overflow: auto;
background-color: #f5f5f5;
border: 1px solid #e0e0e0;
border-radius: 4px;
padding: 15px;
}
.json-viewer pre {
margin: 0;
font-family: 'Courier New', Courier, monospace;
font-size: 13px;
line-height: 1.6;
color: #333;
white-space: pre-wrap;
word-wrap: break-word;
}
</style>
Loading…
Cancel
Save