|
|
<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.orderNo" placeholder="请输入关联单据号" clearable> </el-input> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="关联单据类型"> <el-input v-model="searchForm.interfaceType" 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="6"> <el-form-item label="仓库ID"> <el-input v-model="searchForm.warehouseId" placeholder="请输入仓库ID" clearable> </el-input> </el-form-item> </el-col> <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 type="danger" @click="deleteHandle()" icon="el-icon-delete" size="small"> 批量删除 </el-button> <el-button type="warning" @click="batchRetryInterfaceHandle()" icon="el-icon-refresh-right" 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="60"> </el-table-column> <el-table-column prop="requestId" header-align="center" align="center" label="请求ID" width="100"> </el-table-column> <el-table-column prop="interfaceName" header-align="center" align="left" label="接口名称" min-width="250"> </el-table-column> <el-table-column prop="orderNo" header-align="center" align="left" label="关联单据号" min-width="150"> </el-table-column> <el-table-column prop="interfaceType" header-align="center" align="center" label="关联单据类型" min-width="170"> </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="left" label="消息" min-width="250" 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="warehouseId" header-align="center" align="center" label="仓库ID" width="120"> </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, retryInterface } from '@/api/sys/interface-log'
export default { data() { return { searchExpanded: false, searchForm: { interfaceName: '', requestId: '', orderNo: '', interfaceType: '', statusCode: '', sourceSystem: '', targetSystem: '', needRetryFlag: null, warehouseId: '', dateRange: [], userName: this.$store.state.user.name }, 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, warehouseId: '', dateRange: [], userName: this.$store.state.user.name } 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 || '删除失败') } }) }) },
// 批量手动重试接口
batchRetryInterfaceHandle() { if (this.dataListSelections.length === 0) { this.$message.warning('请选择要重试的记录!') return }
const count = this.dataListSelections.length this.$confirm(`确定要手动重试选中的 ${count} 个接口吗?`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { const loading = this.$loading({ lock: true, text: `正在重试 ${count} 个接口,请稍候...`, spinner: 'el-icon-loading', background: 'rgba(0, 0, 0, 0.7)' })
// 构造批量重试参数
const retryList = this.dataListSelections.map(item => ({ site: item.site, buNo: item.buNo, requestId: item.requestId, requestGroupId: item.requestGroupId, interfaceName: item.interfaceName }))
// 调用批量重试接口
retryInterface({ retryList }).then(({ data }) => { loading.close() if (data && data.code === 0) { const result = data.result const successCount = result.successCount || 0 const failureCount = result.failureCount || 0 const errorCount = result.errorCount || 0
// 显示详细结果
let message = `重试完成!成功:${successCount},失败:${failureCount},异常:${errorCount}`
if (successCount === count) { this.$message.success(message) } else if (successCount > 0) { this.$message.warning(message) } else { this.$message.error(message) }
// 显示详细结果对话框
if (result.details && result.details.length > 0) { this.showRetryResultDialog(result.details) }
this.getDataList() } else { this.$message.error(data.msg || '批量重试失败') } }).catch(err => { loading.close() this.$message.error('批量重试异常:' + (err.message || '请求失败')) }) }) },
// 显示重试结果详情对话框
showRetryResultDialog(details) { const successList = details.filter(d => d.status === 'success') const failureList = details.filter(d => d.status === 'failure') const errorList = details.filter(d => d.status === 'error')
let messageHtml = '<div style="max-height: 400px; overflow-y: auto;">'
if (successList.length > 0) { messageHtml += '<div style="margin-bottom: 15px;"><strong style="color: #67C23A;">成功 (' + successList.length + '):</strong><ul style="margin: 5px 0; padding-left: 20px;">' successList.forEach(item => { messageHtml += '<li>' + item.interfaceName + (item.u8CCode ? ' - U8单号:' + item.u8CCode : '') + '</li>' }) messageHtml += '</ul></div>' }
if (failureList.length > 0) { messageHtml += '<div style="margin-bottom: 15px;"><strong style="color: #E6A23C;">失败 (' + failureList.length + '):</strong><ul style="margin: 5px 0; padding-left: 20px;">' failureList.forEach(item => { messageHtml += '<li>' + item.interfaceName + ':' + (item.message || '未知错误') + '</li>' }) messageHtml += '</ul></div>' }
if (errorList.length > 0) { messageHtml += '<div><strong style="color: #F56C6C;">异常 (' + errorList.length + '):</strong><ul style="margin: 5px 0; padding-left: 20px;">' errorList.forEach(item => { messageHtml += '<li>' + item.interfaceName + ':' + (item.message || '未知异常') + '</li>' }) messageHtml += '</ul></div>' }
messageHtml += '</div>'
this.$alert(messageHtml, '批量重试结果详情', { confirmButtonText: '确定', dangerouslyUseHTMLString: true }) } }}</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>
|