|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="app-container">
|
|
|
|
|
|
<el-row :gutter="10" class="mb8">
|
|
|
|
|
|
<!-- <el-col :span="1.5">
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
plain
|
|
|
|
|
|
@click="handleSummary"
|
|
|
|
|
|
:loading="loading"
|
|
|
|
|
|
v-hasPermi="['warehouse:WmsImportResult:query']"
|
|
|
|
|
|
>获取维修汇总</el-button>
|
|
|
|
|
|
</el-col> -->
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
v-loading="loading"
|
|
|
|
|
|
:data="repairSummaryList"
|
|
|
|
|
|
element-loading-text="正在加载数据..."
|
|
|
|
|
|
border
|
|
|
|
|
|
stripe
|
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="equipment_code" label="设备编号" align="center" width="120">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<span class="equipment-code-link" @click="showRepairRecords(scope.row.equipment_code)">
|
|
|
|
|
|
{{ scope.row.equipment_code }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="equipment_name" label="设备名称" align="center" min-width="200" />
|
|
|
|
|
|
<el-table-column prop="record_count" label="维修次数" align="center" width="100" />
|
|
|
|
|
|
<el-table-column prop="first_apply_time" label="首次维修时间" align="center" min-width="180" />
|
|
|
|
|
|
<el-table-column prop="last_apply_time" label="最近维修时间" align="center" min-width="180" />
|
|
|
|
|
|
<el-table-column label="距离上次维修天数" align="center" width="150">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<el-tag
|
|
|
|
|
|
:type="getDaysTagType(scope.row.days_since_last_repair)"
|
|
|
|
|
|
effect="dark"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ scope.row.days_since_last_repair }} 天
|
|
|
|
|
|
</el-tag>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 设备维修记录对话框 -->
|
|
|
|
|
|
<el-dialog
|
|
|
|
|
|
v-model="repairRecordsDialogVisible"
|
|
|
|
|
|
:title="`${selectedEquipmentCode} 维修记录`"
|
|
|
|
|
|
width="90%"
|
|
|
|
|
|
:before-close="handleRepairRecordsClose">
|
|
|
|
|
|
<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 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>
|
|
|
|
|
|
<span class="dialog-footer">
|
|
|
|
|
|
<el-button @click="repairRecordsDialogVisible = false">关闭</el-button>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
import { ref, onMounted, watch } from 'vue'
|
|
|
|
|
|
import { API_CONFIG } from "@/config/api"
|
|
|
|
|
|
|
|
|
|
|
|
// 接收父组件传递的props
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
selectedEquipmentType: {
|
|
|
|
|
|
type: String,
|
|
|
|
|
|
default: '漆包机'
|
|
|
|
|
|
},
|
|
|
|
|
|
dateRange: {
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: () => ({
|
|
|
|
|
|
start_date: '',
|
|
|
|
|
|
end_date: ''
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const repairSummaryList = ref([])
|
|
|
|
|
|
|
|
|
|
|
|
// 维修记录对话框相关状态
|
|
|
|
|
|
const repairRecordsDialogVisible = ref(false)
|
|
|
|
|
|
const repairRecordsLoading = ref(false)
|
|
|
|
|
|
const selectedRepairRecords = ref([])
|
|
|
|
|
|
const filteredRepairRecords = ref([])
|
|
|
|
|
|
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);
|
|
|
|
|
|
// 如果当前是维修汇总组件,则重新获取数据
|
|
|
|
|
|
if (newVal) {
|
|
|
|
|
|
handleSummary();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听日期范围变化
|
|
|
|
|
|
watch(() => props.dateRange, (newVal, oldVal) => {
|
|
|
|
|
|
console.log('日期范围从', oldVal, '变化为', newVal);
|
|
|
|
|
|
// 如果日期范围发生变化,则重新获取数据
|
|
|
|
|
|
if (newVal && newVal.start_date && newVal.end_date) {
|
|
|
|
|
|
handleSummary();
|
|
|
|
|
|
}
|
|
|
|
|
|
}, { deep: true });
|
|
|
|
|
|
|
|
|
|
|
|
// 监听故障名称变化,确保筛选能正常工作
|
|
|
|
|
|
watch(selectedBreakdownName, (newVal, oldVal) => {
|
|
|
|
|
|
console.log('故障名称从', oldVal, '变化为', newVal);
|
|
|
|
|
|
// 当故障名称变化时,执行筛选
|
|
|
|
|
|
filterRecordsByBreakdownName();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 组件加载时自动获取数据
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
console.log('组件加载,设备类型:', props.selectedEquipmentType);
|
|
|
|
|
|
handleSummary();
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 根据天数获取标签类型
|
|
|
|
|
|
const getDaysTagType = (days) => {
|
|
|
|
|
|
if (days > 300) return 'danger'
|
|
|
|
|
|
if (days > 180) return 'warning'
|
|
|
|
|
|
if (days > 90) return 'success'
|
|
|
|
|
|
return 'info'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取维修汇总数据
|
|
|
|
|
|
function handleSummary() {
|
|
|
|
|
|
console.log('设备类型:', props.selectedEquipmentType);
|
|
|
|
|
|
console.log('日期范围:', props.dateRange);
|
|
|
|
|
|
loading.value = true
|
|
|
|
|
|
|
|
|
|
|
|
// 构建API URL,包含设备类型和日期范围参数
|
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/eq-repair/enamelling-repair-summary?equipment_type=${props.selectedEquipmentType}`;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有日期范围,则添加到URL中
|
|
|
|
|
|
if (props.dateRange && props.dateRange.start_date && props.dateRange.end_date) {
|
|
|
|
|
|
apiUrl += `&start_date=${props.dateRange.start_date}&end_date=${props.dateRange.end_date}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
if (data.code === 200 && data.data) {
|
|
|
|
|
|
repairSummaryList.value = data.data
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('维修汇总API调用失败:', error)
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 显示设备维修记录
|
|
|
|
|
|
const showRepairRecords = (equipmentCode) => {
|
|
|
|
|
|
selectedEquipmentCode.value = equipmentCode
|
|
|
|
|
|
repairRecordsDialogVisible.value = true
|
|
|
|
|
|
fetchRepairRecords(equipmentCode)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取设备维修记录
|
|
|
|
|
|
const fetchRepairRecords = (equipmentCode) => {
|
|
|
|
|
|
repairRecordsLoading.value = true
|
|
|
|
|
|
|
|
|
|
|
|
// 构建API URL
|
|
|
|
|
|
let apiUrl = `${API_CONFIG.BASE_URL}/api/eq-repair/repair-records/${equipmentCode}`;
|
|
|
|
|
|
|
|
|
|
|
|
fetch(apiUrl)
|
|
|
|
|
|
.then(response => response.json())
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
if (data.code === 200 && data.data) {
|
|
|
|
|
|
selectedRepairRecords.value = data.data
|
|
|
|
|
|
filteredRepairRecords.value = data.data
|
|
|
|
|
|
|
|
|
|
|
|
// 提取所有唯一的故障名称
|
|
|
|
|
|
const breakdownNames = [...new Set(data.data.map(record => record.breakdown_name).filter(Boolean))]
|
|
|
|
|
|
uniqueBreakdownNames.value = breakdownNames
|
|
|
|
|
|
|
|
|
|
|
|
// 确保筛选器初始化正确
|
|
|
|
|
|
selectedBreakdownName.value = ''
|
|
|
|
|
|
filteredRepairRecords.value = [...selectedRepairRecords.value]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
selectedRepairRecords.value = []
|
|
|
|
|
|
filteredRepairRecords.value = []
|
|
|
|
|
|
uniqueBreakdownNames.value = []
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(error => {
|
|
|
|
|
|
console.error('维修记录API调用失败:', error)
|
|
|
|
|
|
selectedRepairRecords.value = []
|
|
|
|
|
|
filteredRepairRecords.value = []
|
|
|
|
|
|
uniqueBreakdownNames.value = []
|
|
|
|
|
|
})
|
|
|
|
|
|
.finally(() => {
|
|
|
|
|
|
repairRecordsLoading.value = false
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 根据故障名称筛选维修记录
|
|
|
|
|
|
const filterRecordsByBreakdownName = (value) => {
|
|
|
|
|
|
console.log('筛选故障名称:', value, 'selectedBreakdownName.value:', selectedBreakdownName.value)
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有传入的值,更新 selectedBreakdownName
|
|
|
|
|
|
if (value !== undefined) {
|
|
|
|
|
|
selectedBreakdownName.value = value
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取筛选值,处理空值情况
|
|
|
|
|
|
const filterValue = selectedBreakdownName.value || ''
|
|
|
|
|
|
|
|
|
|
|
|
if (!filterValue) {
|
|
|
|
|
|
// 如果没有选择故障名称,显示所有记录
|
|
|
|
|
|
filteredRepairRecords.value = [...selectedRepairRecords.value]
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 根据选择的故障名称筛选
|
|
|
|
|
|
filteredRepairRecords.value = selectedRepairRecords.value.filter(
|
|
|
|
|
|
record => record.breakdown_name === filterValue
|
|
|
|
|
|
)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('筛选后记录数:', filteredRepairRecords.value.length)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭维修记录对话框
|
|
|
|
|
|
const handleRepairRecordsClose = () => {
|
|
|
|
|
|
repairRecordsDialogVisible.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查维修日期是否是最近的(30天内)
|
|
|
|
|
|
const isRecent = (repairDate) => {
|
|
|
|
|
|
if (!repairDate) return false
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const repairDateObj = new Date(repairDate)
|
|
|
|
|
|
const currentDate = new Date()
|
|
|
|
|
|
const timeDiff = currentDate - repairDateObj
|
|
|
|
|
|
const daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24))
|
|
|
|
|
|
|
|
|
|
|
|
return daysDiff <= 30
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 计算维修时长
|
|
|
|
|
|
const calculateRepairDuration = (applyTime, repairTime) => {
|
|
|
|
|
|
if (!applyTime || !repairTime) return '无'
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const applyDate = new Date(applyTime)
|
|
|
|
|
|
const repairDate = new Date(repairTime)
|
|
|
|
|
|
const timeDiff = repairDate - applyDate
|
|
|
|
|
|
|
|
|
|
|
|
if (timeDiff < 0) return '无效时间'
|
|
|
|
|
|
|
|
|
|
|
|
const hours = Math.floor(timeDiff / (1000 * 60 * 60))
|
|
|
|
|
|
const minutes = Math.floor((timeDiff % (1000 * 60 * 60)) / (1000 * 60))
|
|
|
|
|
|
|
|
|
|
|
|
if (hours > 24) {
|
|
|
|
|
|
const days = Math.floor(hours / 24)
|
|
|
|
|
|
const remainingHours = hours % 24
|
|
|
|
|
|
return `${days}天${remainingHours}小时`
|
|
|
|
|
|
} else if (hours > 0) {
|
|
|
|
|
|
return `${hours}小时${minutes}分钟`
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return `${minutes}分钟`
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
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>
|
|
|
|
|
|
.app-container {
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mb8 {
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 设备编号链接样式 */
|
|
|
|
|
|
.equipment-code-link {
|
|
|
|
|
|
color: #409EFF;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
text-decoration: underline;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.equipment-code-link:hover {
|
|
|
|
|
|
color: #66b1ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 故障名称筛选区域样式 */
|
|
|
|
|
|
.breakdown-filter-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
|
padding: 0 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.filter-label {
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 维修记录对话框样式 */
|
|
|
|
|
|
.repair-records-container {
|
|
|
|
|
|
padding: 10px 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.no-records {
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
padding: 20px 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 最近维修记录高亮样式 */
|
|
|
|
|
|
.recent {
|
|
|
|
|
|
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>
|