You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

976 lines
34 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>
<el-button
type="success"
@click="analyzeWeibull"
:loading="weibullAnalyzing"
style="margin-left: 10px;"
>分段Weibull分析</el-button>
<el-button
type="warning"
@click="analyzeNHPP"
:loading="nhppAnalyzing"
style="margin-left: 10px;"
>分段NHPP分析</el-button>
</div>
<el-tabs type="border-card" v-model="analysisTab">
<el-tab-pane label="维修分析" name="repairAnalysis">
<el-input
v-model="analysisResult"
type="textarea"
:rows="15"
placeholder="点击分析维修记录按钮后,分析结果将显示在这里"
readonly
/>
</el-tab-pane>
<el-tab-pane label="Weibull分析" name="weibullAnalysis">
<el-input
v-model="weibullResult"
type="textarea"
:rows="15"
placeholder="点击分段Weibull分析按钮后分析结果将显示在这里"
readonly
/>
</el-tab-pane>
<el-tab-pane label="NHPP分析" name="nhppAnalysis">
<el-input
v-model="nhppResult"
type="textarea"
:rows="15"
placeholder="点击分段NHPP分析按钮后分析结果将显示在这里"
readonly
/>
</el-tab-pane>
</el-tabs>
</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('')
const weibullAnalyzing = ref(false)
const weibullResult = ref('')
const nhppAnalyzing = ref(false)
const nhppResult = ref('')
const analysisTab = ref('repairAnalysis')
// 监听设备类型变化
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
// 清空之前的分析结果
analysisResult.value = ''
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
// 清空分析结果
analysisResult.value = ''
}
// 检查维修日期是否是最近的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) // 模拟分析过程耗时
}
// 分段Weibull分析
const analyzeWeibull = async () => {
if (!selectedEquipmentCode.value) {
weibullResult.value = '请先选择设备。'
return
}
weibullAnalyzing.value = true
try {
// 构建API参数
const params = new URLSearchParams({
equipment_code: selectedEquipmentCode.value,
window_size: '5'
})
// 添加日期范围参数
if (props.dateRange && props.dateRange.start_date) {
params.append('start_date', props.dateRange.start_date)
}
if (props.dateRange && props.dateRange.end_date) {
params.append('end_date', props.dateRange.end_date)
}
// 添加故障名称筛选
if (selectedBreakdownName.value) {
params.append('breakdown_name_filter', selectedBreakdownName.value)
}
const apiUrl = `${API_CONFIG.BASE_URL}/api/eq-repair/weibull/analysis?${params.toString()}`
console.log('Weibull分析API URL:', apiUrl)
const response = await fetch(apiUrl)
const data = await response.json()
console.log('Weibull分析返回数据:', data)
if (data.code === 200 && data.data) {
if (data.data.original_count === 0 || data.data.valid_count === 0) {
weibullResult.value = `=== 设备 ${selectedEquipmentCode.value} 分段Weibull分析报告 ===\n\n` +
`【分析结果】\n` +
`- 在指定的时间范围内,该设备没有足够的维修记录数据进行分析。\n` +
`- 可能原因:\n` +
` 1. 该设备在所选日期范围内没有维修记录\n` +
` 2. 故障记录数据不完整\n` +
`- 建议:\n` +
` 1. 尝试扩大日期范围\n` +
` 2. 选择其他设备进行分析\n` +
` 3. 检查该设备的实际维修记录\n\n` +
`API返回信息: ${JSON.stringify(data.data, null, 2)}`
} else {
weibullResult.value = formatWeibullResult(data.data)
}
} else if (data.detail) {
weibullResult.value = `API错误: ${data.detail}`
} else if (data.message) {
weibullResult.value = `API消息: ${data.message}`
} else {
weibullResult.value = `未能获取到有效的分析结果。\n\n原始响应: ${JSON.stringify(data, null, 2)}`
}
} catch (error) {
console.error('Weibull分析API调用失败:', error)
weibullResult.value = `分析失败: ${error.message}`
} finally {
weibullAnalyzing.value = false
}
}
// 分段NHPP分析
const analyzeNHPP = async () => {
if (!selectedEquipmentCode.value) {
nhppResult.value = '请先选择设备。'
return
}
nhppAnalyzing.value = true
try {
const params = new URLSearchParams({
equipment_code: selectedEquipmentCode.value,
breakdown_name_filter: selectedBreakdownName.value || '收线故障',
window_size: '5'
})
if (props.dateRange && props.dateRange.start_date) {
params.append('start_date', props.dateRange.start_date)
}
if (props.dateRange && props.dateRange.end_date) {
params.append('end_date', props.dateRange.end_date)
}
const apiUrl = `${API_CONFIG.BASE_URL}/api/eq-repair/nhpp/analysis?${params.toString()}`
console.log('NHPP分析API URL:', apiUrl)
const response = await fetch(apiUrl)
const data = await response.json()
console.log('NHPP分析返回数据:', data)
if (data.code === 200 && data.data) {
if (data.data.record_count === 0) {
nhppResult.value = `=== 设备 ${selectedEquipmentCode.value} 分段NHPP分析报告 ===\n\n` +
`【分析结果】\n` +
`- 在指定的时间范围内,该设备没有足够的维修记录数据进行分析。\n` +
`- 可能原因:\n` +
` 1. 该设备在所选日期范围内没有维修记录\n` +
` 2. 故障记录数据不完整\n` +
`- 建议:\n` +
` 1. 尝试扩大日期范围\n` +
` 2. 选择其他设备进行分析\n` +
` 3. 检查该设备的实际维修记录\n\n` +
`API返回信息: ${JSON.stringify(data.data, null, 2)}`
} else {
nhppResult.value = formatNHPPResult(data.data)
}
} else if (data.detail) {
nhppResult.value = `API错误: ${data.detail}`
} else if (data.message) {
nhppResult.value = `API消息: ${data.message}`
} else {
nhppResult.value = `未能获取到有效的分析结果。\n\n原始响应: ${JSON.stringify(data, null, 2)}`
}
} catch (error) {
console.error('NHPP分析API调用失败:', error)
nhppResult.value = `分析失败: ${error.message}`
} finally {
nhppAnalyzing.value = false
}
}
// 格式化NHPP分析结果
const formatNHPPResult = (data) => {
let result = `=== 设备 ${data.equipment_code || selectedEquipmentCode.value} 分段NHPP分析报告 ===\n`
result += `【消息】${data.message || '分析完成'}\n\n`
result += `【基本统计】\n`
result += `- 设备编号: ${data.equipment_code || 'N/A'}\n`
result += `- 维修记录数: ${data.record_count || 0}\n`
result += `- 分析窗口大小: ${data.window_size || 'N/A'}\n`
result += `- 间隔数: ${data.interval_count || 0}\n`
result += `- 平均间隔天数: ${data.avg_interval_days?.toFixed(2) || 'N/A'}\n`
result += `- 标准差间隔天数: ${data.std_interval_days?.toFixed(2) || 'N/A'}\n`
result += `- 当前运行天数: ${data.current_runtime_days?.toFixed(2) || 'N/A'}\n\n`
result += `【NHPP模型参数】\n`
result += `- 模型类型: ${data.model_type || 'N/A'}\n`
result += `- 形状参数(β): ${data.current_nhpp_beta?.toFixed(4) || 'N/A'}\n`
result += `- 尺度参数(η): ${data.current_nhpp_eta?.toFixed(2) || 'N/A'}\n\n`
if (data.predictions) {
result += `【失效概率预测】\n`
const pred = data.predictions
if (pred['7_days_failure_prob'] !== undefined) {
result += `- 7天失效概率: ${pred['7_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['15_days_failure_prob'] !== undefined) {
result += `- 15天失效概率: ${pred['15_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['30_days_failure_prob'] !== undefined) {
result += `- 30天失效概率: ${pred['30_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['60_days_failure_prob'] !== undefined) {
result += `- 60天失效概率: ${pred['60_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['90_days_failure_prob'] !== undefined) {
result += `- 90天失效概率: ${pred['90_days_failure_prob'].toFixed(2)}%\n`
}
result += `\n`
}
result += `【分析结论】\n`
if (data.current_nhpp_beta !== undefined) {
const beta = data.current_nhpp_beta
let trend = ''
let advice = ''
if (beta < 1) {
trend = '故障率随时间下降(设备处于调试/磨合期)'
advice = '建议:关注早期失效模式,优化初始设置和调试过程'
} else if (beta >= 1 && beta < 1.2) {
trend = '故障率相对稳定(随机失效期)'
advice = '建议:执行计划性维护,保持当前维护策略'
} else {
trend = '故障率随时间上升(磨损失效期)'
advice = '建议:加强预防性维护,考虑关键部件的预防更换'
}
result += `- 故障趋势: ${trend}\n`
result += `- 维护建议: ${advice}\n`
}
if (data.current_nhpp_eta !== undefined) {
const eta = data.current_nhpp_eta
result += `- 特征寿命(η): ${eta.toFixed(2)} 天,约 ${(eta / 30).toFixed(1)} 个月\n`
result += ` (NHPP模型下63.2%设备预计在此时间内失效)\n`
}
if (data.avg_interval_days !== undefined && data.std_interval_days !== undefined) {
result += `- 平均维修间隔: ${data.avg_interval_days.toFixed(2)}\n`
const cv = (data.std_interval_days / data.avg_interval_days * 100).toFixed(1)
result += `- 间隔波动(变异系数): ${cv}%\n`
if (parseFloat(cv) < 30) {
result += `- 评估: 维修间隔较稳定,维护计划可预测性良好\n`
} else {
result += `- 评估: 维修间隔波动较大,需分析影响维修间隔的因素\n`
}
}
if (data.current_runtime_days !== undefined && data.current_nhpp_eta !== undefined) {
const runtime = data.current_runtime_days
const eta = data.current_nhpp_eta
const ratio = (runtime / eta * 100).toFixed(1)
result += `\n- 当前累计运行: ${runtime.toFixed(2)} 天 (占特征寿命的 ${ratio}%)\n`
if (parseFloat(ratio) > 50) {
result += `- 预警: 设备已超过特征寿命50%,建议加强状态监测\n`
}
}
result += `\n`
return result
}
// 格式化Weibull分析结果
const formatWeibullResult = (data) => {
let result = `=== 设备 ${data.equipment_code || selectedEquipmentCode.value} 分段Weibull分析报告 ===\n`
result += `【消息】${data.message || '分析完成'}\n\n`
// 基本统计
result += `【基本统计】\n`
result += `- 设备编号: ${data.equipment_code || 'N/A'}\n`
result += `- 维修记录数: ${data.record_count || 0}\n`
result += `- 分析窗口大小: ${data.window_size || 'N/A'}\n`
result += `- 间隔数: ${data.interval_count || 0}\n`
result += `- 平均间隔天数: ${data.avg_interval_days?.toFixed(2) || 'N/A'}\n`
result += `- 标准差间隔天数: ${data.std_interval_days?.toFixed(2) || 'N/A'}\n`
result += `- 当前运行天数: ${data.current_runtime_days?.toFixed(2) || 'N/A'}\n\n`
// Weibull参数
result += `【Weibull分布参数】\n`
result += `- 模型类型: ${data.model_type || 'N/A'}\n`
result += `- 形状参数(β): ${data.current_weibull_beta?.toFixed(4) || 'N/A'}\n`
result += `- 尺度参数(η): ${data.current_weibull_eta?.toFixed(2) || 'N/A'}\n\n`
// 失效概率预测
if (data.predictions) {
result += `【失效概率预测】\n`
const pred = data.predictions
if (pred['7_days_failure_prob'] !== undefined) {
result += `- 7天失效概率: ${pred['7_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['15_days_failure_prob'] !== undefined) {
result += `- 15天失效概率: ${pred['15_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['30_days_failure_prob'] !== undefined) {
result += `- 30天失效概率: ${pred['30_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['60_days_failure_prob'] !== undefined) {
result += `- 60天失效概率: ${pred['60_days_failure_prob'].toFixed(2)}%\n`
}
if (pred['90_days_failure_prob'] !== undefined) {
result += `- 90天失效概率: ${pred['90_days_failure_prob'].toFixed(2)}%\n`
}
result += `\n`
}
// 分析结论
result += `【分析结论】\n`
if (data.current_weibull_beta !== undefined) {
const beta = data.current_weibull_beta
let reliabilityTrend = ''
let maintenanceAdvice = ''
if (beta < 1) {
reliabilityTrend = '设备处于"早期失效期",故障率随时间下降'
maintenanceAdvice = '建议:加强初期调试和磨合期监控,及时更换早期失效的部件'
} else if (beta >= 1 && beta < 1.5) {
reliabilityTrend = '设备处于"随机失效期",故障率相对稳定'
maintenanceAdvice = '建议:执行预防性维护,保持当前维护计划'
} else {
reliabilityTrend = '设备处于"磨损失效期",故障率随时间增加'
maintenanceAdvice = '建议:考虑预防性更换关键部件,制定大修计划'
}
result += `- 可靠性趋势: ${reliabilityTrend}\n`
result += `- 维护建议: ${maintenanceAdvice}\n`
}
if (data.current_weibull_eta !== undefined) {
const eta = data.current_weibull_eta
result += `- 特征寿命(η): ${eta.toFixed(2)} 天,约 ${(eta / 30).toFixed(1)} 个月\n`
result += ` (表示63.2%设备预计在此时间内失效的时间点)\n`
}
if (data.avg_interval_days !== undefined && data.std_interval_days !== undefined) {
result += `- 平均维修间隔: ${data.avg_interval_days.toFixed(2)}\n`
result += `- 间隔标准差: ${data.std_interval_days.toFixed(2)}\n`
const cv = (data.std_interval_days / data.avg_interval_days * 100).toFixed(1)
result += `- 变异系数: ${cv}%\n`
if (parseFloat(cv) < 30) {
result += `- 维修间隔较稳定,维护计划可预测性高\n`
} else {
result += `- 维修间隔波动较大,建议进一步分析影响维修间隔的因素\n`
}
}
if (data.current_runtime_days !== undefined) {
const eta = data.current_weibull_eta || 0
const runtime = data.current_runtime_days
if (eta > 0) {
const ratio = (runtime / eta * 100).toFixed(1)
result += `\n- 当前已运行: ${runtime.toFixed(2)} 天 (占特征寿命的 ${ratio}%)\n`
if (parseFloat(ratio) > 50) {
result += `- 警告设备已超过特征寿命的50%,建议密切关注设备状态\n`
}
}
}
result += `\n`
return result
}
</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>