xyc
3 天以前 53052625c7c520744a4ca061e204ec9594ca67f8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
<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>