|
|
<template> <div class="mod-config"> <!-- 查询表单 --> <el-form :inline="true" label-position="top" style="margin-top: 1px; margin-left: 0px;">
<el-form-item label="物料编号"> <el-input v-model="queryData.partNo" style="width: 120px" placeholder="物料编号" clearable></el-input> </el-form-item>
<el-form-item label="批次号"> <el-input v-model="queryData.lotBatchNo" style="width: 120px" placeholder="批次号" clearable></el-input> </el-form-item>
<el-form-item label="接口名称"> <el-select v-model="queryData.interfaceName" placeholder="请选择" style="width: 150px" clearable> <el-option label="全部" value=""></el-option> <el-option label="MoveInventoryPart" value="MoveInventoryPart"></el-option> <el-option label="IssueInventoryPart" value="IssueInventoryPart"></el-option> <el-option label="ReturnInventoryPart" value="ReturnInventoryPart"></el-option> </el-select> </el-form-item>
<el-form-item label="方法类型"> <el-select v-model="queryData.methodType" placeholder="请选择" style="width: 120px" clearable> <el-option label="全部" value=""></el-option> <el-option label="库存移库(栈板)" value="库存移库(栈板)"></el-option> <el-option label="发料" value="发料"></el-option> <el-option label="退料" value="退料"></el-option> </el-select> </el-form-item>
<el-form-item label="处理状态"> <el-select v-model="queryData.processStatus" placeholder="请选择" style="width: 120px" clearable> <el-option label="全部" value=""></el-option> <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option> </el-select> </el-form-item>
<el-form-item label="创建时间从"> <el-date-picker v-model="queryData.startDate" type="date" style="width: 120px" placeholder="开始日期" value-format="yyyy-MM-dd" clearable> </el-date-picker> </el-form-item>
<el-form-item label="创建时间至"> <el-date-picker v-model="queryData.endDate" type="date" style="width: 120px" placeholder="结束日期" value-format="yyyy-MM-dd" clearable> </el-date-picker> </el-form-item>
<el-form-item label=" "> <el-button type="primary" @click="getDataList()">查询</el-button> <el-button @click="resetQuery()">重置</el-button> <!-- rqrq - 一键重试按钮(后端批量处理) --> <el-button type="warning" :loading="batchRetrying" :disabled="batchRetrying || selectedRows.length === 0" @click="handleBatchRetry()"> {{ batchRetrying ? '批量重试中...' : `一键重试(${selectedRows.length})` }} </el-button> <!-- 导出按钮 - rqrq --> <download-excel :fields="fields()" :data="exportData" type="xls" :name="exportName" :header="exportHeader" :footer="exportFooter" :fetch="createExportData" :before-generate="startDownload" :before-finish="finishDownload" worksheet="导出信息" class="el-button el-button--primary el-button--medium"> {{ '导出' }} </download-excel> </el-form-item> </el-form>
<!-- 数据表格 --> <el-table ref="dataTable" :data="dataList" :height="height" border highlight-current-row v-loading="dataListLoading" @selection-change="handleSelectionChange" style="width: 100%;"> <!-- rqrq - 多选列,只允许选择PENDING状态的记录 --> <el-table-column type="selection" width="50" :selectable="checkSelectable"> </el-table-column> <el-table-column v-for="(item,index) in columnList" :key="index" :sortable="item.columnSortable" :prop="item.columnProp" :header-align="item.headerAlign" :show-overflow-tooltip="item.showOverflowTooltip" :align="item.align" :fixed="item.fixed==''?false:item.fixed" :min-width="item.columnWidth" :label="item.columnLabel"> <template slot-scope="scope"> <span v-if="item.columnProp === 'processStatus'"> <el-tag :type="getStatusType(scope.row.processStatus)" size="small"> {{ getStatusLabel(scope.row.processStatus) }} </el-tag> </span> <span v-else-if="item.columnProp === 'operate'"> <el-button v-if="scope.row.processStatus === 'PENDING'" type="text" size="small" @click="showDetailDialog(scope.row)"> 查看详情 </el-button> <!-- rqrq - 使用全局重试状态控制按钮 --> <el-button v-if="scope.row.processStatus === 'PENDING'" type="text" size="small" style="color: #E6A23C;" :loading="retryingRowId === scope.row.id" :disabled="retryingRowId !== null" @click="handleRetry(scope.row)"> {{ retryingRowId === scope.row.id ? '重试中...' : '重试' }} </el-button> <span v-else>-</span> </span> <span v-else>{{ scope.row[item.columnProp] }}</span> </template> </el-table-column>
<el-table-column header-align="center" align="center" fixed="right" width="180" label="操作"> <template slot-scope="scope"> <a type="text" size="small" @click="showDetailDialog(scope.row)">查看详情</a> <!-- rqrq - 当前行正在重试时显示"重试中..." --> <span v-if="scope.row.processStatus === 'PENDING' && retryingRowId === scope.row.id" style="color: #E6A23C; margin-left: 10px;">重试中...</span> <!-- rqrq - 其他行有重试时,显示禁用状态的重试按钮 --> <span v-else-if="scope.row.processStatus === 'PENDING' && retryingRowId !== null" style="color: #C0C4CC; margin-left: 10px; cursor: not-allowed;" @click="handleRetry(scope.row)">重试</span> <!-- rqrq - 没有重试时,显示正常的重试按钮 --> <a type="text" size="small" v-else-if="scope.row.processStatus === 'PENDING'" @click="handleRetry(scope.row)">重试</a><!-- <a type="text" size="small" v-if="scope.row.processStatus === 'PENDING'" @click="closeRetry(scope.row)">忽略</a>--> </template> </el-table-column> </el-table>
<!-- 分页 --> <el-pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" :current-page="pageIndex" :page-sizes="[20, 50, 100, 1000]" :page-size="pageSize" :total="totalPage" layout="total, sizes, prev, pager, next, jumper"> </el-pagination>
<!-- 详情弹窗 - rqrq --> <el-dialog title="IFS错误日志详情" :visible.sync="detailVisible" width="70%" :close-on-click-modal="false" v-drag>
<el-form label-width="120px" style="padding: 10px;"> <!-- 基本信息 - rqrq --> <el-row> <el-col :span="8"> <el-form-item label="工厂编码:"> <span>{{ currentRow.site }}</span> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="物料编号:"> <span>{{ currentRow.partNo }}</span> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="批次号:"> <span>{{ currentRow.lotBatchNo }}</span> </el-form-item> </el-col> </el-row>
<el-row> <el-col :span="8"> <el-form-item label="源库位:"> <span>{{ currentRow.sourceLocation }}</span> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="目标库位:"> <span>{{ currentRow.destLocation }}</span> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="数量:"> <span>{{ currentRow.qty }}</span> </el-form-item> </el-col> </el-row>
<el-row> <el-col :span="8"> <el-form-item label="接口名称:"> <span>{{ currentRow.interfaceName }}</span> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="方法类型:"> <span>{{ currentRow.methodType }}</span> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="创建时间:"> <span>{{ currentRow.createdAt }}</span> </el-form-item> </el-col> </el-row>
<!-- 错误信息 - rqrq --> <el-form-item label="错误信息:"> <el-input type="textarea" :rows="5" resize='none' v-model="currentRow.errorMessage" readonly> </el-input> </el-form-item>
<!-- 请求数据 - rqrq --> <el-form-item label="请求数据:" style="margin-top: 90px"> <el-input type="textarea" :rows="3" resize='none' v-model="currentRow.requestData" readonly> </el-input> </el-form-item> <el-form-item label="标签:" style="margin-top: 55px"> <el-input type="textarea" :rows="2" resize='none' v-model="currentRow.serialNos" readonly> </el-input> </el-form-item>
<!-- 响应数据 - rqrq --> <el-form-item label="响应数据:" style="margin-top: 40px"> <el-input type="textarea" :rows="2" resize='none' v-model="currentRow.responseData" readonly> </el-input> </el-form-item>
<!-- 处理备注 - rqrq --> <el-form-item label="处理备注:" style="margin-top: 40px"> <el-input type="textarea" :rows="2" resize='none' v-model="processRemark" placeholder="请输入处理说明(标记为已处理或已忽略时必填)"> </el-input> </el-form-item> </el-form>
<div slot="footer" class="dialog-footer" style="margin-top: 25px"> <el-button type="success" @click="markAsProcessed" :loading="markProcessedLoading" :disabled="markProcessedLoading || markIgnoredLoading"> {{ markProcessedLoading ? '处理中...' : '标记为已处理' }} </el-button> <el-button type="warning" @click="markAsIgnored" :loading="markIgnoredLoading" :disabled="markProcessedLoading || markIgnoredLoading"> {{ markIgnoredLoading ? '处理中...' : '标记为已忽略' }} </el-button> <el-button @click="detailVisible = false" :disabled="markProcessedLoading || markIgnoredLoading"> 关闭 </el-button> </div> </el-dialog> </div></template>
<script>import { getIfsErrorLogList, markErrorAsProcessed, markErrorAsIgnored, getUserAuthorizedSites, retryIfsCall, closeIfsCall, batchRetryIfsCall} from '@/api/warehouse/ifsCallErrorLog.js'
export default { data() { return { // 查询条件 - rqrq
queryData: { site: '', partNo: '', lotBatchNo: '', interfaceName: '', methodType: '', processStatus: '', startDate: '', endDate: '', page: 1, limit: 20 }, // 表格高度 - rqrq
height: 450, // 状态选项 - rqrq
statusOptions: [ { label: '待处理', value: 'PENDING' }, { label: '已处理', value: 'PROCESSED' }, { label: '已忽略', value: 'IGNORED' } ], // 站点选项 - rqrq
siteOptions: [], // 表格列配置 - rqrq
columnList: [ { columnProp: "site", headerAlign: "center", align: "center", columnLabel: "工厂", columnWidth: 60, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "partNo", headerAlign: "center", align: "center", columnLabel: "物料编号", columnWidth: 120, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "lotBatchNo", headerAlign: "center", align: "center", columnLabel: "批次号", columnWidth: 120, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "sourceLocation", headerAlign: "center", align: "center", columnLabel: "源库位", columnWidth: 100, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "destLocation", headerAlign: "center", align: "center", columnLabel: "目标库位", columnWidth: 100, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "qty", headerAlign: "center", align: "center", columnLabel: "数量", columnWidth: 80, columnSortable: false, showOverflowTooltip: true, fixed: "" },
{ columnProp: "serialNos", headerAlign: "center", align: "center", columnLabel: "标签", columnWidth: 260, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "processStatus", headerAlign: "center", align: "center", columnLabel: "处理状态", columnWidth: 100, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "retryCount", headerAlign: "center", align: "center", columnLabel: "重试次数", columnWidth: 80, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "errorMessage", headerAlign: "center", align: "left", columnLabel: "错误信息", columnWidth: 200, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "createdAt", headerAlign: "center", align: "center", columnLabel: "创建时间", columnWidth: 150, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "processedAt", headerAlign: "center", align: "center", columnLabel: "处理时间", columnWidth: 150, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "processedBy", headerAlign: "center", align: "center", columnLabel: "处理人", columnWidth: 100, columnSortable: false, showOverflowTooltip: true, fixed: "" }, { columnProp: "remark", headerAlign: "center", align: "left", columnLabel: "备注", columnWidth: 150, columnSortable: false, showOverflowTooltip: true, fixed: "" },
], // 数据列表 - rqrq
dataList: [], // 分页参数 - rqrq
pageIndex: 1, pageSize: 20, totalPage: 0, dataListLoading: false, // 详情弹窗 - rqrq
detailVisible: false, currentRow: {}, processRemark: '', markProcessedLoading: false, markIgnoredLoading: false, retryingRowId: null, // 正在重试的行ID,全局唯一 - rqrq
// 批量重试相关 - rqrq
selectedRows: [], // 选中的行
batchRetrying: false, // 批量重试中
batchRetryProgress: 0, // 批量重试进度
batchRetryTotal: 0, // 批量重试总数
// 导出相关 - rqrq
exportData: [], exportName: 'IFS错误日志' + this.dayjs().format('YYYYMMDDHHmmss'), exportHeader: ["IFS错误日志"], exportFooter: [], } }, mounted() { this.$nextTick(() => { // 计算表格高度 - rqrq
this.height = window.innerHeight - 220; }) }, activated() { this.getDataList() this.getSiteOptions() }, methods: { // 获取数据列表 - rqrq
getDataList() { this.dataListLoading = true this.queryData.page = this.pageIndex this.queryData.limit = this.pageSize
getIfsErrorLogList(this.queryData).then(({data}) => { this.dataListLoading = false if (data && data.code === 0) { this.dataList = data.page.list || [] this.totalPage = data.page.totalCount || 0 } else { this.dataList = [] this.totalPage = 0 this.$alert(data.msg || '查询失败', '错误', { confirmButtonText: '确定' }) } }).catch(error => { this.dataListLoading = false console.error('查询IFS错误日志失败:', error) this.$alert('查询失败', '错误', { confirmButtonText: '确定' }) }) },
// 获取站点选项 - rqrq
getSiteOptions() { const params = { userName: this.$store.state.user.name } getUserAuthorizedSites(params).then(({data}) => { if (data && data.code === 0) { this.siteOptions = data.data || [] } }).catch(error => { console.error('获取站点列表失败:', error) }) },
// 重置查询条件 - rqrq
resetQuery() { this.queryData = { site: '', partNo: '', lotBatchNo: '', interfaceName: '', methodType: '', processStatus: '', startDate: '', endDate: '', page: 1, limit: 20 } this.pageIndex = 1 this.getDataList() },
// 每页数量变化 - rqrq
sizeChangeHandle(val) { this.pageSize = val this.pageIndex = 1 this.getDataList() },
// 当前页变化 - rqrq
currentChangeHandle(val) { this.pageIndex = val this.getDataList() },
// 获取状态标签类型 - rqrq
getStatusType(status) { const statusMap = { 'PENDING': 'warning', 'PROCESSED': 'success', 'IGNORED': 'info' } return statusMap[status] || 'info' },
// 获取状态标签文本 - rqrq
getStatusLabel(status) { const statusMap = { 'PENDING': '待处理', 'PROCESSED': '已处理', 'IGNORED': '已忽略' } return statusMap[status] || status },
// 显示详情弹窗 - rqrq
showDetailDialog(row) { this.currentRow = { ...row } this.processRemark = '' this.detailVisible = true },
// 标记为已处理 - rqrq
markAsProcessed() { if (!this.processRemark || this.processRemark.trim() === '') { this.$message.warning('请填写处理备注') return }
this.markProcessedLoading = true const params = { id: this.currentRow.id, remark: this.processRemark }
markErrorAsProcessed(params).then(({data}) => { if (data && data.code === 0) { this.$message.success('标记成功') this.detailVisible = false this.getDataList() } else { this.$alert(data.msg || '操作失败', '错误', { confirmButtonText: '确定' }) } }).catch(error => { console.error('标记失败:', error) this.$alert('操作失败', '错误', { confirmButtonText: '确定' }) }).finally(() => { this.markProcessedLoading = false }) },
// 标记为已忽略 - rqrq
markAsIgnored() { if (!this.processRemark || this.processRemark.trim() === '') { this.$message.warning('请填写忽略原因') return }
this.markIgnoredLoading = true const params = { id: this.currentRow.id, remark: this.processRemark }
markErrorAsIgnored(params).then(({data}) => { if (data && data.code === 0) { this.$message.success('标记成功') this.detailVisible = false this.getDataList() } else { this.$alert(data.msg || '操作失败', '错误', { confirmButtonText: '确定' }) } }).catch(error => { console.error('标记失败:', error) this.$alert('操作失败', '错误', { confirmButtonText: '确定' }) }).finally(() => { this.markIgnoredLoading = false }) }, closeRetry(row){ this.$confirm('确定要忽略此记录的IFS接口调用吗?', '确认重试', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { const params = { id: row.id }
closeIfsCall(params).then(({data}) => { if (data && data.code === 0) { this.$message.success('忽略成功') // 刷新列表 - rqrq
this.getDataList() } else { this.$alert(data.msg || '忽略失败', '错误', { confirmButtonText: '确定' }) // 刷新列表(更新重试次数)- rqrq
this.getDataList() } }) }) },
// 手工重试IFS接口调用 - rqrq
// 多选变化处理 - rqrq
handleSelectionChange(selection) { // 只保留PENDING状态的记录 - rqrq
this.selectedRows = selection.filter(row => row.processStatus === 'PENDING') },
// 检查行是否可选(只允许选择PENDING状态)- rqrq
checkSelectable(row) { return row.processStatus === 'PENDING' },
// 延时函数 - rqrq
sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)) },
// 批量重试(调用后端接口,由后端按时间顺序执行)- rqrq
handleBatchRetry() { if (this.selectedRows.length === 0) { this.$message.warning('请先选择要重试的记录') return }
// 检查是否有单个重试正在进行 - rqrq
if (this.retryingRowId !== null) { this.$message.warning('请等待当前重试完成后再操作') return }
this.$confirm(`确定要重试选中的 ${this.selectedRows.length} 条记录吗?将按时间顺序从最早开始逐个重试。`, '批量重试确认', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { // 获取选中行的ID列表 - rqrq
const ids = this.selectedRows.map(row => row.id)
// 开始批量重试 - rqrq
this.batchRetrying = true this.batchRetryTotal = ids.length this.batchRetryProgress = 0
// 调用后端批量重试接口 - rqrq
batchRetryIfsCall({ ids }).then(({data}) => { if (data && data.code === 0) { const successCount = data.successCount || 0 const failCount = data.failCount || 0
// 显示结果 - rqrq
this.$alert( `批量重试完成!<br/>成功:<span style="color: #67C23A; font-weight: bold;">${successCount}</span> 条<br/>失败:<span style="color: #F56C6C; font-weight: bold;">${failCount}</span> 条`, '重试结果', { dangerouslyUseHTMLString: true, confirmButtonText: '确定' } ) } else { this.$alert(data.msg || '批量重试失败', '错误', { confirmButtonText: '确定' }) } }).catch(error => { console.error('批量重试失败:', error) this.$alert('批量重试失败,请稍后再试', '错误', { confirmButtonText: '确定' }) }).finally(() => { // 重试完成,重置状态 - rqrq
this.batchRetrying = false this.batchRetryProgress = 0 this.batchRetryTotal = 0 this.selectedRows = [] // 刷新列表 - rqrq
this.getDataList() }) }).catch(() => { // 用户取消操作
}) },
handleRetry(row) { // 检查是否有批量重试正在进行 - rqrq
if (this.batchRetrying) { this.$message.warning('批量重试进行中,请等待完成') return }
// 检查是否有其他行正在重试 - rqrq
if (this.retryingRowId !== null) { this.$message.warning('请等待当前重试完成后再操作') return }
this.$confirm('确定要重试此记录的IFS接口调用吗?', '确认重试', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { // 设置全局重试状态 - rqrq
this.retryingRowId = row.id this.$set(row, '_retrying', true)
const params = { id: row.id }
retryIfsCall(params).then(({data}) => { if (data && data.code === 0) { this.$message.success('重试成功,IFS接口调用完成') // 刷新列表 - rqrq
this.getDataList() } else { this.$alert(data.msg || '重试失败', '错误', { confirmButtonText: '确定' }) // 刷新列表(更新重试次数)- rqrq
this.getDataList() } }).catch(error => { console.error('重试失败:', error) this.$alert('重试失败,请稍后再试', '错误', { confirmButtonText: '确定' }) // 刷新列表 - rqrq
this.getDataList() }).finally(() => { // 取消loading状态 - rqrq
this.$set(row, '_retrying', false) this.retryingRowId = null }) }).catch(() => { // 用户取消操作
}) }, // 导出相关方法 - rqrq
async createExportData() { const queryParams = { ...this.queryData, page: 1, limit: 999999 // 设置一个很大的数字来获取全部数据 - rqrq
} const {data} = await getIfsErrorLogList(queryParams) if (data && data.code === 0) { return (data.page.list || []).map(item => { return { ...item, processStatus: this.getStatusLabel(item.processStatus) } }) } return [] }, startDownload() { // 开始导出 - rqrq
}, finishDownload() { // 导出完成 - rqrq
}, fields() { let json = "{" this.columnList.forEach((item, index) => { if (index == this.columnList.length - 1) { json += "\"" + item.columnLabel + "\"" + ":" + "\"" + item.columnProp + "\"" } else { json += "\"" + item.columnLabel + "\"" + ":" + "\"" + item.columnProp + "\"" + "," } }) json += "}" let s = eval("(" + json + ")") return s } }}</script>
<style scoped>/* 表格样式优化 - rqrq */.mod-config { padding: 10px;}</style>
|