<template>
|
<div class="page">
|
<div class="page-title">
|
<div>
|
<h2>{{ title }}</h2>
|
<p>{{ subtitle }}</p>
|
</div>
|
<el-button icon="el-icon-refresh" @click="load">刷新</el-button>
|
</div>
|
|
<div v-if="type === 'overview'" class="stat-grid">
|
<div class="stat-card"><div class="label">工程总数</div><div class="value">{{ stats.engineeringTotal || 0 }}</div></div>
|
<div class="stat-card"><div class="label">重大故障</div><div class="value">{{ stats.majorFault || 0 }}</div></div>
|
<div class="stat-card"><div class="label">涉H统计</div><div class="value">{{ stats.hRelated || 0 }}</div></div>
|
<div class="stat-card"><div class="label">监测项目</div><div class="value">{{ stats.monitorItem || 0 }}</div></div>
|
<div class="stat-card"><div class="label">未完工</div><div class="value">{{ stats.unfinished || 0 }}</div></div>
|
<div class="stat-card"><div class="label">已完工</div><div class="value">{{ stats.completed || 0 }}</div></div>
|
<div class="stat-card"><div class="label">完工待试验</div><div class="value">{{ stats.waitTest || 0 }}</div></div>
|
<div class="stat-card"><div class="label">销项工程</div><div class="value">{{ stats.cancelled || 0 }}</div></div>
|
</div>
|
|
<section class="report-panel">
|
<div class="panel-head"><h3>{{ chartTitle }}</h3></div>
|
<div ref="chart" class="chart-box"></div>
|
</section>
|
|
<section class="report-panel">
|
<div class="panel-head"><h3>统计明细</h3><span>共 {{ rows.length }} 条</span></div>
|
<el-table :data="rows" border stripe size="small">
|
<el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label" :min-width="column.width || 110" show-overflow-tooltip></el-table-column>
|
</el-table>
|
</section>
|
</div>
|
</template>
|
<script>
|
import * as echarts from 'echarts'
|
import api from '@/api'
|
|
export default {
|
name: 'ReportPage',
|
data() {
|
return { stats: {}, rows: [], chart: null }
|
},
|
computed: {
|
type() { return this.$route.params.type },
|
title() {
|
return {
|
overview: '装备故障总体分析',
|
'top-equipment': '故障高发设备Top',
|
'by-team': '按T统计',
|
contractors: '厂家维修情况',
|
trend: '故障数 / 备品备件消耗年度趋势'
|
}[this.type] || '统计报表'
|
},
|
subtitle() {
|
return {
|
overview: '上方柱状图、下方统计表,指标覆盖工程总数和完工状态',
|
'top-equipment': '贯穿各T,通过设备基础库归集设备类别',
|
'by-team': '按T汇总故障数和备品备件消耗',
|
contractors: '厂家与承修单位同义,统计工程总数、未完工、已完工和打分情况',
|
trend: '贯穿各T,展示故障数和备品备件消耗年度趋势'
|
}[this.type]
|
},
|
chartTitle() {
|
return this.type === 'trend' ? '年度趋势图' : this.type === 'contractors' ? '厂家维修对比' : '指标分布'
|
},
|
columns() {
|
if (this.type === 'overview') return [
|
{ prop: 'name', label: '统计指标' },
|
{ prop: 'value', label: '数量' }
|
]
|
if (this.type === 'top-equipment') return [
|
{ prop: 'name', label: '设备名称', width: 180 },
|
{ prop: 'value', label: '故障数', width: 100 }
|
]
|
if (this.type === 'by-team') return [
|
{ prop: 'name', label: 'T队专业', width: 150 },
|
{ prop: 'faultCount', label: '故障数' },
|
{ prop: 'partsCount', label: '备品备件消耗' }
|
]
|
if (this.type === 'contractors') return [
|
{ prop: 'name', label: '承修单位', width: 180 },
|
{ prop: 'total', label: '工程总数' },
|
{ prop: 'unfinished', label: '未完工' },
|
{ prop: 'completed', label: '已完工' },
|
{ prop: 'averageScore', label: '平均打分' }
|
]
|
return [
|
{ prop: 'name', label: '年度', width: 120 },
|
{ prop: 'faultCount', label: '故障数' },
|
{ prop: 'partsCount', label: '备品备件消耗' }
|
]
|
}
|
},
|
watch: {
|
'$route.params.type': 'load'
|
},
|
mounted() {
|
this.load()
|
window.addEventListener('resize', this.resize)
|
},
|
beforeDestroy() {
|
window.removeEventListener('resize', this.resize)
|
if (this.chart) this.chart.dispose()
|
},
|
methods: {
|
load() {
|
const requests = {
|
overview: api.get('/reports/overview').then(res => { this.stats = res.data || {}; this.rows = this.stats.chart || [] }),
|
'top-equipment': api.get('/reports/top-equipment').then(res => { this.rows = res.data || []; this.stats = {} }),
|
'by-team': api.get('/reports/by-team').then(res => { this.rows = res.data || []; this.stats = {} }),
|
contractors: api.get('/reports/contractors').then(res => { this.rows = res.data || []; this.stats = {} }),
|
trend: api.get('/reports/yearly-trend').then(res => { this.rows = res.data || []; this.stats = {} })
|
}
|
;(requests[this.type] || Promise.resolve()).then(() => this.$nextTick(this.renderChart))
|
},
|
renderChart() {
|
if (!this.$refs.chart) return
|
if (!this.chart) this.chart = echarts.init(this.$refs.chart)
|
const names = this.rows.map(item => item.name)
|
const first = this.rows.map(item => item.value != null ? item.value : item.faultCount)
|
const second = this.type === 'overview' ? null : this.rows.map(item => item.partsCount != null ? item.partsCount : item.total)
|
const option = {
|
color: this.type === 'trend' ? ['#2c7fb8', '#e29b3b'] : ['#2c7fb8', '#69a85f'],
|
tooltip: { trigger: 'axis' },
|
legend: second && ['by-team', 'trend'].includes(this.type) ? { data: ['故障数', '备品备件消耗'] } : undefined,
|
grid: { left: 48, right: 28, top: 38, bottom: 62 },
|
xAxis: { type: 'category', data: names, axisLabel: { interval: 0, rotate: names.length > 7 ? 25 : 0 } },
|
yAxis: { type: 'value', minInterval: 1, splitLine: { lineStyle: { color: '#e8eef4' } } },
|
series: []
|
}
|
if (this.type === 'trend') {
|
option.series = [
|
{ name: '故障数', type: 'line', smooth: true, data: first, areaStyle: { opacity: .08 } },
|
{ name: '备品备件消耗', type: 'bar', barMaxWidth: 38, data: second }
|
]
|
} else if (['by-team'].includes(this.type)) {
|
option.series = [
|
{ name: '故障数', type: 'bar', barMaxWidth: 42, data: first },
|
{ name: '备品备件消耗', type: 'bar', barMaxWidth: 42, data: second }
|
]
|
} else if (this.type === 'contractors') {
|
option.series = [
|
{ name: '工程总数', type: 'bar', barMaxWidth: 42, data: first },
|
{ name: '已完工', type: 'bar', barMaxWidth: 42, data: second }
|
]
|
} else {
|
option.series = [{ type: 'bar', barMaxWidth: 42, data: first, itemStyle: { borderRadius: [3, 3, 0, 0] } }]
|
}
|
this.chart.setOption(option, true)
|
},
|
resize() { if (this.chart) this.chart.resize() }
|
}
|
}
|
</script>
|
<style scoped>
|
.report-panel { margin-top: 12px; padding: 14px; border: 1px solid #dce4ec; border-radius: 4px; background: #fff; }
|
.panel-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
|
.panel-head h3 { margin: 0; color: #31465d; font-size: 15px; }
|
.panel-head span { color: #8796a7; font-size: 12px; }
|
</style>
|