添加维修记录分析功能,优化对话框布局为左右分栏,支持故障统计、维修效率分析和建议生成

master
huangjinysf 2 months ago
parent f9fd5e6488
commit 0b60ba7275

@ -47,61 +47,89 @@
<el-dialog
v-model="repairRecordsDialogVisible"
:title="`${selectedEquipmentCode} 维修记录`"
width="80%"
width="90%"
:before-close="handleRepairRecordsClose">
<!-- 故障名称筛选 -->
<div class="breakdown-filter-container">
<span class="filter-label">故障名称</span>
<el-select
v-model="selectedBreakdownName"
placeholder="全部故障"
clearable
@change="filterRecordsByBreakdownName"
@clear="filterRecordsByBreakdownName"
>
<el-option
v-for="name in uniqueBreakdownNames"
:key="name"
:label="name"
:value="name"
/>
</el-select>
</div>
<div v-loading="repairRecordsLoading" class="repair-records-container">
<div v-if="selectedRepairRecords.length > 0">
<el-table
:data="filteredRepairRecords"
stripe
border
style="width: 100%"
max-height="60vh">
<el-table-column prop="id" label="维修编号" width="80" align="center" />
<el-table-column prop="breakdown_name" label="故障名称" width="120" align="center" />
<el-table-column prop="breakdown_description" label="故障描述" min-width="150" show-overflow-tooltip />
<el-table-column prop="repair_method" label="维修方法" min-width="150" show-overflow-tooltip />
<el-table-column prop="apply_time" label="报修时间" width="200" align="center">
<template #default="scope">
<span :class="{ 'recent': isRecent(scope.row.repair_time) }">
{{ scope.row.apply_time || '无' }}
</span>
</template>
</el-table-column>
<el-table-column prop="repair_time" label="维修时间" width="200" align="center">
<template #default="scope">
<span :class="{ 'recent': isRecent(scope.row.repair_time) }">
{{ scope.row.repair_time || '无' }}
</span>
</template>
</el-table-column>
<el-table-column label="维修时长" width="160" align="center">
<template #default="scope">
{{ calculateRepairDuration(scope.row.apply_time, scope.row.repair_time) }}
</template>
</el-table-column>
</el-table>
<div class="repair-records-dialog-container">
<!-- 左侧数据展示区域 -->
<div class="left-panel">
<!-- 故障名称筛选 -->
<div class="breakdown-filter-container">
<span class="filter-label">故障名称</span>
<el-select
v-model="selectedBreakdownName"
placeholder="全部故障"
clearable
@change="filterRecordsByBreakdownName"
@clear="filterRecordsByBreakdownName"
>
<el-option
v-for="name in uniqueBreakdownNames"
:key="name"
:label="name"
:value="name"
/>
</el-select>
</div>
<div v-loading="repairRecordsLoading" class="repair-records-container">
<div v-if="selectedRepairRecords.length > 0">
<el-table
:data="filteredRepairRecords"
stripe
border
style="width: 100%"
max-height="60vh">
<el-table-column prop="id" label="维修编号" width="80" align="center" />
<el-table-column prop="breakdown_name" label="故障名称" width="120" align="center" />
<el-table-column prop="breakdown_description" label="故障描述" min-width="150" show-overflow-tooltip />
<el-table-column prop="repair_method" label="维修方法" min-width="150" show-overflow-tooltip />
<el-table-column prop="apply_time" label="报修时间" width="200" align="center">
<template #default="scope">
<span :class="{ 'recent': isRecent(scope.row.repair_time) }">
{{ scope.row.apply_time || '无' }}
</span>
</template>
</el-table-column>
<el-table-column prop="repair_time" label="维修时间" width="200" align="center">
<template #default="scope">
<span :class="{ 'recent': isRecent(scope.row.repair_time) }">
{{ scope.row.repair_time || '无' }}
</span>
</template>
</el-table-column>
<el-table-column label="维修时长" width="160" align="center">
<template #default="scope">
{{ calculateRepairDuration(scope.row.apply_time, scope.row.repair_time) }}
</template>
</el-table-column>
</el-table>
</div>
<div v-else-if="!repairRecordsLoading" class="no-records">
暂无维修记录
</div>
</div>
</div>
<div v-else-if="!repairRecordsLoading" class="no-records">
暂无维修记录
<!-- 右侧分析结果区域 -->
<div class="right-panel">
<!-- 分析按钮 -->
<div class="analysis-button-container">
<el-button
type="primary"
@click="analyzeRepairRecords"
:loading="analyzing"
>分析维修记录</el-button>
</div>
<!-- 分析结果显示区域 -->
<div class="analysis-result-container">
<el-input
v-model="analysisResult"
type="textarea"
:rows="20"
placeholder="点击分析维修记录按钮后,分析结果将显示在这里"
readonly
/>
</div>
</div>
</div>
<template #footer>
@ -144,6 +172,10 @@ const selectedEquipmentCode = ref('')
const selectedBreakdownName = ref('')
const uniqueBreakdownNames = ref([])
//
const analyzing = ref(false)
const analysisResult = ref('')
//
watch(() => props.selectedEquipmentType, (newVal, oldVal) => {
console.log('设备类型从', oldVal, '变化为', newVal);
@ -330,6 +362,153 @@ const calculateRepairDuration = (applyTime, repairTime) => {
return '计算错误'
}
}
//
const analyzeRepairRecords = () => {
analyzing.value = true
//
setTimeout(() => {
try {
if (filteredRepairRecords.value.length === 0) {
analysisResult.value = '暂无数据可供分析。'
analyzing.value = false
return
}
//
const totalRecords = filteredRepairRecords.value.length
const breakdownStats = {}
const repairMethods = {}
const repairDurations = []
const recentRecords = []
const oldestRecords = []
let totalRepairTime = 0
let completedRepairs = 0
//
filteredRepairRecords.value.forEach(record => {
//
const breakdownName = record.breakdown_name || '未知故障'
breakdownStats[breakdownName] = (breakdownStats[breakdownName] || 0) + 1
//
const repairMethod = record.repair_method || '未知方法'
repairMethods[repairMethod] = (repairMethods[repairMethod] || 0) + 1
//
if (record.apply_time && record.repair_time) {
const applyDate = new Date(record.apply_time)
const repairDate = new Date(record.repair_time)
if (!isNaN(applyDate.getTime()) && !isNaN(repairDate.getTime()) && repairDate > applyDate) {
const duration = (repairDate - applyDate) / (1000 * 60 * 60) //
repairDurations.push(duration)
totalRepairTime += duration
completedRepairs++
}
}
})
//
let result = `=== 设备 ${selectedEquipmentCode.value} 维修记录分析报告 ===\n\n`
// 1.
result += `【基本统计】\n`
result += `- 总维修记录数:${totalRecords}\n`
result += `- 完成维修数:${completedRepairs}\n`
if (completedRepairs > 0) {
const avgRepairTime = (totalRepairTime / completedRepairs).toFixed(2)
result += `- 平均维修时长:${avgRepairTime} 小时\n`
}
result += `\n`
// 2.
result += `【故障类型分析】\n`
const sortedBreakdowns = Object.entries(breakdownStats).sort((a, b) => b[1] - a[1])
sortedBreakdowns.forEach(([name, count]) => {
const percentage = ((count / totalRecords) * 100).toFixed(1)
result += `- ${name}${count} 次 (${percentage}%)\n`
})
result += `\n`
// 3.
result += `【维修方法分析】\n`
const sortedMethods = Object.entries(repairMethods).sort((a, b) => b[1] - a[1])
if (sortedMethods.length > 0) {
sortedMethods.slice(0, 5).forEach(([method, count]) => {
const percentage = ((count / totalRecords) * 100).toFixed(1)
result += `- ${method}${count} 次 (${percentage}%)\n`
})
} else {
result += `- 暂无维修方法记录\n`
}
result += `\n`
// 4.
if (repairDurations.length > 0) {
result += `【维修效率分析】\n`
repairDurations.sort((a, b) => a - b)
const minTime = repairDurations[0].toFixed(2)
const maxTime = repairDurations[repairDurations.length - 1].toFixed(2)
const medianTime = repairDurations[Math.floor(repairDurations.length / 2)].toFixed(2)
result += `- 最短维修时间:${minTime} 小时\n`
result += `- 最长维修时间:${maxTime} 小时\n`
result += `- 中位数维修时间:${medianTime} 小时\n`
//
let fastRepairs = 0, normalRepairs = 0, slowRepairs = 0
repairDurations.forEach(duration => {
if (duration <= 2) fastRepairs++
else if (duration <= 8) normalRepairs++
else slowRepairs++
})
result += `- 快速维修≤2小时${fastRepairs} 次 (${((fastRepairs / repairDurations.length) * 100).toFixed(1)}%)\n`
result += `- 常规维修2-8小时${normalRepairs} 次 (${((normalRepairs / repairDurations.length) * 100).toFixed(1)}%)\n`
result += `- 耗时维修(>8小时${slowRepairs} 次 (${((slowRepairs / repairDurations.length) * 100).toFixed(1)}%)\n`
} else {
result += `【维修效率分析】\n`
result += `- 暂无有效维修时间记录\n`
}
result += `\n`
// 5.
result += `【建议与结论】\n`
//
if (sortedBreakdowns.length > 0) {
const topBreakdown = sortedBreakdowns[0]
const percentage = ((topBreakdown[1] / totalRecords) * 100).toFixed(1)
result += `- 最常见的故障是"${topBreakdown[0]}",占比${percentage}%,建议针对性加强预防措施\n`
}
//
if (repairDurations.length > 0) {
const avgTime = totalRepairTime / completedRepairs
if (avgTime > 8) {
result += `- 平均维修时间较长,建议优化维修流程或增加维修人员\n`
} else if (avgTime < 2) {
result += `- 维修效率较高,维修团队表现良好\n`
}
}
//
if (selectedBreakdownName.value) {
const filteredCount = filteredRepairRecords.value.length
const totalCount = selectedRepairRecords.value.length
result += `- 针对"${selectedBreakdownName.value}"故障的专项分析:共${filteredCount}条记录,占总故障的${((filteredCount / totalCount) * 100).toFixed(1)}%\n`
}
analysisResult.value = result
} catch (error) {
console.error('分析过程中出错:', error)
analysisResult.value = '分析过程中发生错误,请稍后重试。'
} finally {
analyzing.value = false
}
}, 1500) //
}
</script>
<style scoped>
@ -381,4 +560,47 @@ const calculateRepairDuration = (applyTime, repairTime) => {
color: #F56C6C;
font-weight: bold;
}
/* 维修记录对话框左右布局样式 */
.repair-records-dialog-container {
display: flex;
height: 60vh;
}
.left-panel {
flex: 3;
padding-right: 15px;
display: flex;
flex-direction: column;
}
.right-panel {
flex: 2;
padding-left: 15px;
border-left: 1px solid #ebeef5;
display: flex;
flex-direction: column;
}
.analysis-button-container {
margin-bottom: 15px;
text-align: right;
}
.analysis-result-container {
flex: 1;
display: flex;
flex-direction: column;
}
.analysis-result-container .el-textarea {
flex: 1;
}
.analysis-result-container .el-textarea__inner {
height: 100%;
font-family: monospace;
white-space: pre-wrap;
word-break: break-word;
}
</style>
Loading…
Cancel
Save