<template>
|
<div class="page dashboard-page">
|
<div class="page-title">
|
<div>
|
<h2>首页门户</h2>
|
<p>故障任务、派工单和完工验收的实时概览</p>
|
</div>
|
<el-button size="small" icon="el-icon-refresh" @click="loadAll">刷新</el-button>
|
</div>
|
|
<div class="stat-grid">
|
<div class="stat-card" v-for="item in heroStats" :key="item.label">
|
<div class="label">{{ item.label }}</div>
|
<div class="value">{{ item.value }}</div>
|
<div class="stat-note">{{ item.note }}</div>
|
</div>
|
</div>
|
|
<div class="dashboard-grid">
|
<section class="dashboard-panel chart-panel">
|
<div class="panel-head">
|
<h3>装备故障总体分析</h3>
|
<span>上方柱状图 / 下方统计</span>
|
</div>
|
<div ref="overviewChart" class="chart-box"></div>
|
</section>
|
<section class="dashboard-panel quick-panel">
|
<div class="panel-head"><h3>常用功能</h3></div>
|
<div class="quick-list">
|
<button @click="$router.push('/fault/report')"><i class="el-icon-edit-outline"></i><span>故障上报</span></button>
|
<button @click="$router.push('/fault/review')"><i class="el-icon-s-check"></i><span>XCB助理审核</span></button>
|
<button @click="$router.push('/fault/dispatch')"><i class="el-icon-s-promotion"></i><span>一键派单</span></button>
|
<button @click="$router.push('/fault/completion')"><i class="el-icon-finished"></i><span>完工处理</span></button>
|
<button @click="$router.push('/acceptance/application')"><i class="el-icon-document-checked"></i><span>验收申请</span></button>
|
<button @click="$router.push('/reports/overview')"><i class="el-icon-data-analysis"></i><span>统计报表</span></button>
|
</div>
|
</section>
|
</div>
|
|
<section class="dashboard-panel">
|
<div class="panel-head">
|
<h3>按X号统计</h3>
|
<span>按X号汇总当前任务和完工状态</span>
|
</div>
|
<el-table :data="xStats" border size="small">
|
<el-table-column prop="xNo" label="X号" width="64"></el-table-column>
|
<el-table-column prop="currentStatus" label="当前状态" width="110">
|
<template slot-scope="{ row }"><el-tag size="mini" type="info">{{ row.currentStatus }}</el-tag></template>
|
</el-table-column>
|
<el-table-column prop="berth" label="停靠泊位" width="120"></el-table-column>
|
<el-table-column prop="team" label="TD" min-width="130"></el-table-column>
|
<el-table-column prop="total" label="工程总数" align="center"></el-table-column>
|
<el-table-column prop="cancelled" label="销项总数" align="center"></el-table-column>
|
<el-table-column prop="major" label="重大故障" align="center"></el-table-column>
|
<el-table-column prop="unfinished" label="未完工" align="center"></el-table-column>
|
<el-table-column prop="waitTest" label="完工待试验" align="center"></el-table-column>
|
<el-table-column prop="completed" label="已完工" align="center"></el-table-column>
|
<el-table-column prop="pending" label="待处理" align="center"></el-table-column>
|
</el-table>
|
</section>
|
|
<div class="bottom-grid">
|
<section class="dashboard-panel">
|
<div class="panel-head"><h3>最近故障</h3><el-button type="text" @click="$router.push('/fault/report')">查看全部</el-button></div>
|
<div class="recent-list">
|
<div v-for="item in recentFaults" :key="item.id" class="recent-item">
|
<div>
|
<strong>{{ item.faultCode }}</strong>
|
<span>{{ item.xNo }} · {{ item.equipmentName }}</span>
|
</div>
|
<el-tag size="mini" :type="taskMeta(item.taskStatus).type">{{ taskMeta(item.taskStatus).text }}</el-tag>
|
</div>
|
</div>
|
</section>
|
<section class="dashboard-panel">
|
<div class="panel-head"><h3>公告</h3></div>
|
<ul class="notice-list">
|
<li><b>状态控制</b>任务状态、完工状态、验收状态分别独立维护,不使用流程引擎。</li>
|
<li><b>派单规则</b>故障编号由系统自动生成;派工单编号为“故障编号/01、/02”。</li>
|
<li><b>验收前置</b>只有完工状态为“完工”的派工单才能进入验收流程。</li>
|
</ul>
|
</section>
|
<section class="dashboard-panel">
|
<div class="panel-head"><h3>共享资源下载</h3></div>
|
<div class="resource-list">
|
<span><i class="el-icon-document"></i>完工信息填报说明</span>
|
<span><i class="el-icon-document"></i>质量评分口径</span>
|
<span><i class="el-icon-document"></i>常用承修单位名录</span>
|
</div>
|
</section>
|
</div>
|
</div>
|
</template>
|
<script>
|
import * as echarts from 'echarts'
|
import api from '@/api'
|
import { taskStatuses, statusMeta } from '@/utils/dict'
|
|
export default {
|
name: 'Dashboard',
|
data() {
|
return {
|
overview: {},
|
faults: [],
|
workOrders: [],
|
chart: null
|
}
|
},
|
computed: {
|
heroStats() {
|
return [
|
{ label: '工程总数', value: this.overview.engineeringTotal || 0, note: '当前故障任务' },
|
{ label: '重大故障', value: this.overview.majorFault || 0, note: '需重点关注' },
|
{ label: '涉H统计', value: this.overview.hRelated || 0, note: '敏感标识任务' },
|
{ label: '未完工', value: this.overview.unfinished || 0, note: '派工单口径' },
|
{ label: '完工待试验', value: this.overview.waitTest || 0, note: '待试验工单' },
|
{ label: '已完工', value: this.overview.completed || 0, note: '可进入验收' }
|
]
|
},
|
recentFaults() {
|
return this.faults.slice(0, 6)
|
},
|
xStats() {
|
const map = {}
|
const ensure = xNo => {
|
if (!map[xNo]) map[xNo] = { xNo, currentStatus: '小修', berth: '--', team: 'T01', total: 0, cancelled: 0, major: 0, unfinished: 0, waitTest: 0, completed: 0, pending: 0 }
|
return map[xNo]
|
}
|
this.faults.forEach(item => {
|
const row = ensure(item.xNo || '未填写')
|
row.total += 1
|
if (item.taskStatus === 'CANCELLED') row.cancelled += 1
|
if (item.faultNature === '重大') row.major += 1
|
if (!item.contractorIds) row.pending += 1
|
row.team = item.teamMajor || row.team
|
})
|
this.workOrders.forEach(item => {
|
const row = ensure(item.xNo || '未填写')
|
if (item.completionStatus === 'COMPLETED') row.completed += 1
|
else if (item.completionStatus === 'WAIT_TEST') row.waitTest += 1
|
else row.unfinished += 1
|
})
|
return Object.values(map)
|
}
|
},
|
mounted() {
|
this.loadAll()
|
window.addEventListener('resize', this.resizeChart)
|
},
|
beforeDestroy() {
|
window.removeEventListener('resize', this.resizeChart)
|
if (this.chart) this.chart.dispose()
|
},
|
methods: {
|
taskMeta(value) { return statusMeta(taskStatuses, value) },
|
loadAll() {
|
Promise.all([
|
api.get('/reports/overview'),
|
api.get('/faults'),
|
api.get('/work-orders')
|
]).then(([overview, faults, orders]) => {
|
this.overview = overview.data || {}
|
this.faults = faults.data || []
|
this.workOrders = orders.data || []
|
this.$nextTick(this.renderChart)
|
})
|
},
|
renderChart() {
|
if (!this.$refs.overviewChart) return
|
if (!this.chart) this.chart = echarts.init(this.$refs.overviewChart)
|
const chartData = this.overview.chart || []
|
this.chart.setOption({
|
color: ['#2c7fb8'],
|
tooltip: { trigger: 'axis' },
|
grid: { left: 40, right: 20, top: 28, bottom: 48 },
|
xAxis: { type: 'category', data: chartData.map(item => item.name), axisLabel: { interval: 0, rotate: 22 } },
|
yAxis: { type: 'value', minInterval: 1, splitLine: { lineStyle: { color: '#e8eef4' } } },
|
series: [{ type: 'bar', barMaxWidth: 42, data: chartData.map(item => item.value), itemStyle: { borderRadius: [3, 3, 0, 0] } }]
|
})
|
},
|
resizeChart() {
|
if (this.chart) this.chart.resize()
|
}
|
}
|
}
|
</script>
|
<style scoped>
|
.dashboard-grid { display: grid; grid-template-columns: minmax(0, 2fr) 330px; gap: 12px; margin-top: 12px; }
|
.dashboard-panel { margin-top: 12px; padding: 14px; border: 1px solid #dce4ec; border-radius: 4px; background: #fff; }
|
.dashboard-grid .dashboard-panel { margin-top: 0; }
|
.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: #8a98a8; font-size: 12px; }
|
.stat-note { margin-top: 4px; color: #93a1b0; font-size: 12px; }
|
.quick-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; }
|
.quick-list button { display: flex; align-items: center; gap: 9px; padding: 15px 10px; border: 1px solid #dce4ec; border-radius: 4px; color: #31465d; background: #f8fafc; cursor: pointer; text-align: left; }
|
.quick-list button:hover { border-color: #79aee0; color: #1769aa; background: #f1f7fd; }
|
.quick-list i { font-size: 20px; color: #2c7fb8; }
|
.recent-item { display: flex; align-items: center; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid #edf1f5; }
|
.recent-item:last-child { border-bottom: 0; }
|
.recent-item strong { display: block; color: #263b52; font-size: 13px; }
|
.recent-item span { color: #7e8e9f; font-size: 12px; }
|
.bottom-grid { display: grid; grid-template-columns: 1.2fr 1fr 1fr; gap: 12px; }
|
.notice-list { margin: 0; padding-left: 18px; color: #5f7185; font-size: 13px; line-height: 24px; }
|
.notice-list b { color: #2f4966; }
|
.resource-list { display: grid; gap: 9px; color: #42617f; font-size: 13px; }
|
.resource-list i { margin-right: 7px; color: #2c7fb8; }
|
@media (max-width: 1100px) { .dashboard-grid, .bottom-grid { grid-template-columns: 1fr; } }
|
</style>
|