dbd-meeting-html/src/views/admin/repair/RepairStatistics.vue
2024-08-28 21:04:08 +08:00

858 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="container">
<div class="topHeadView">
<div class="itemView">
<img class="imgView" :src='baseUrl + "/img/paidan.png"'>
<div class="label">待派单</div>
<div class="num">{{ adminStatsCount.wait }}</div>
</div>
<div class="itemView">
<div class="imgContainer">
<img class="imgView" :src='baseUrl + "/img/paidanjump.png"'>
</div>
<div class="label">重派单</div>
<div class="num">{{ adminStatsCount.anew }}</div>
</div>
<div class="itemView">
<img class="imgView" :src='baseUrl + "/img/time.png"'>
<div class="label">未查看通知</div>
<div class="num">{{ adminStatsCount.newnotice }}</div>
</div>
<div class="itemView">
<img class="imgView" :src='baseUrl + "/img/info.png"'>
<div class="label">已查看通知</div>
<div class="num">{{ adminStatsCount.oldnotice }}</div>
</div>
</div>
<div class="caseStatisticsView">
<div class="headView">
<div class="titleView">工单统计</div>
<div class="typeView" style="width: 200px;">
<a-range-picker v-model="dateRange2" @change="onChange2" />
</div>
</div>
<div class="contentView">
<div class="itemView">
<div class="label">维修工单</div>
<div class="value">{{ repairStatsCount.zs }}</div>
</div>
<div class="itemView">
<div class="label">待维修工单</div>
<div class="value">{{ repairStatsCount.wait }}</div>
</div>
<div class="itemView">
<div class="label">超时量</div>
<div class="value">{{ repairStatsCount.timeout }}</div>
</div>
<div class="itemView">
<div class="label">差评量</div>
<div class="value">{{ repairStatsCount.l }}</div>
</div>
<div class="itemView">
<div class="label">中评量</div>
<div class="value">{{ repairStatsCount.m }}</div>
</div>
<div class="itemView">
<div class="label">好评量</div>
<div class="value">{{ repairStatsCount.h }}</div>
</div>
</div>
</div>
<div class="statusView">
<div class="caseStatusView">
<div class="headView">
<div class="titleView">工单完成情况</div>
<div class="typeView" style="width: 200px;">
<a-range-picker v-model="dateRange3L" @change="onChange3L" />
</div>
</div>
<div class="contentView">
<div class="circularView">
<div id="echart1" style="width: 400px; height: 300px"></div>
</div>
<div class="centerLine"></div>
<div class="circularView">
<div id="echart2" style="width: 400px; height: 300px"></div>
</div>
</div>
</div>
<div class="evaluateView">
<div class="headView">
<div class="titleView">评价情况</div>
<div class="typeView" style="width: 200px;">
<a-range-picker v-model="dateRange3R" @change="onChange3R" />
</div>
</div>
<div class="contentView">
<div id="echart3" style="width: 400px; height: 300px"></div>
</div>
</div>
</div>
<div class="faultView">
<div class="floorView">
<div class="headView">
<div class="titleView">网格长</div>
<div class="typeView" style="width: 200px;">
<a-range-picker v-model="dateRange4L" @change="onChange4L" />
</div>
</div>
<div class="contentView">
<div id="echart4" style="width: 550px; height: 600px"></div>
</div>
</div>
<div class="rightView">
<div class="blockView">
<div class="headView">
<div class="titleView">故障统计</div>
<div class="typeView" style="width: 200px;">
<a-range-picker v-model="dateRange4R1" @change="onChange4R1" />
</div>
</div>
<div class="contentView">
<div id="echart5" style="width: 700px; height: 327px"></div>
</div>
</div>
<div class="blockView">
<div class="headView">
<div class="titleView">故障类型</div>
<div class="typeView" style="width: 200px;">
<a-range-picker v-model="dateRange4R2" @change="onChange4R2" />
</div>
</div>
<div class="contentView">
<div id="echart6" style="width: 700px; height: 327px"></div>
</div>
</div>
</div>
</div>
<div class="tableView">
<div class="headView">
<div class="titleView">项目详情</div>
<div class="typeView" style="width: 200px;">
<a-range-picker v-model="dateRange5" @change="onChange5" />
</div>
</div>
<div class="contentView">
<a-table
size="default"
ref="table"
style="width: 100%"
:columns="columns"
:data-source="loadData"
>
</a-table>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import moment from 'moment'
import { STable } from '@/components'
import {
repairAdminStats,
repairStats,
floorStats,
deviceStats,
failureStats,
deviceTypeStats
} from '@/api/admin/repair/repairStats'
//常量计算当前周的开始与结束
const initStart=moment().startOf('isoWeek'),initEnd=moment().endOf('isoWeek');
export default {
name: 'RepairStatistics',
components: {
STable
},
data () {
return {
dateRange2: [initStart, initEnd],
dateRange3L: [initStart, initEnd],
dateRange3R: [initStart, initEnd],
dateRange4L: [initStart, initEnd],
dateRange4R1: [initStart, initEnd],
dateRange4R2: [initStart, initEnd],
dateRange5: [initStart, initEnd],
baseUrl: process.env.BASE_URL,
adminStatsCount: [],
repairStatsCount: [],
finishCount: [],
timeout: [],
deviceTypeCount: [],
pj: [],
floorStatsCount: [],
// 查询参数
queryParam: {},
selectedRowKeys: [],
selectedRows: [],
faultStatsCount: [],
selectFaultStatsCount: [],
deviceType: {
type: '',
time: '',
open: false
},
fault: {
type: '',
time: '',
open: false
},
yearShowOne: false,
//
floor: {
type: '',
time: '',
open: false
},
faultType: {
type: '',
time: '',
open: false
},
// 表头
columns: [
{
title: '类型',
dataIndex: 'typename'
},
{
title: '保修次数',
dataIndex: 'zs'
},
{
title: '维修次数',
dataIndex: 'closed'
},
{
title: '损坏最多品牌',
dataIndex: 'brand'
},
{
title: '维修完成率',
dataIndex: 'wcl'
},
{
title: '好评率',
dataIndex: 'hl'
},
{
title: '中评率',
dataIndex: 'ml'
},
{
title: '差评率',
dataIndex: 'll'
}
],
loadData: []
}
},
mounted () {
this.adminStats()//第一行工单统计
this.repairCount2()//第二行 工单统计
this.repairCount3L()//三行 工单完成情况
this.repairCount3R()//三行 评价情况
this.floorStats()//第四行 左侧 楼层负责人情况统计
this.deviceStats()// 第四行 右上设备 故障统计
this.failureStats()//第四行 右下 故障类型统计
this.deviceTypeStats()//第五行 设备、品牌、评价 统计
},
methods: {
onChange2 () {
this.repairCount2();
},
onChange3L() {
this.repairCount3L();
},
onChange3R() {
this.repairCount3R();
},
onChange4L(){
this.floorStats();
},
onChange4R1(){
this.deviceStats();
},
onChange4R2(){
this.failureStats();
},
onChange5(){
this.deviceTypeStats();
},
adminStats () {
repairAdminStats().then(res => {
this.adminStatsCount = res.repairAdminStats
})
},
repairCount2 (){
const startDate = this.dateRange2[0].format('YYYY-MM-DD');
const endDate = this.dateRange2[1].format('YYYY-MM-DD');
repairStats({ startDate: startDate, endDate: endDate, type: 'repair' }).then(res => {
this.repairStatsCount = res.data.stats
})
},
repairCount3L (){
const startDate = this.dateRange3L[0].format('YYYY-MM-DD');
const endDate = this.dateRange3L[1].format('YYYY-MM-DD');
repairStats({startDate: startDate, endDate: endDate, type: 'end'}).then(res => {
this.finishCount = res.data.wc
this.timeout = res.data.timeout
this.createEchartCake('echart1', {
bottom: 10,
left: 'right',
orient: 'vertical'
}, ['#91cd77', '#ef6567'], this.finishCount)
this.createEchartCake('echart2', {
bottom: 10,
left: 'right',
orient: 'vertical'
}, ['#ef6567', '#fc8251', '#f9c956', '#91cd77'], this.timeout)
})
},
repairCount3R (){
const startDate = this.dateRange3R[0].format('YYYY-MM-DD');
const endDate = this.dateRange3R[1].format('YYYY-MM-DD');
repairStats({ startDate: startDate, endDate: endDate, type: 'eval' }).then(res => {
this.pj = res.data.pj
this.createEchartCake('echart3', {
bottom: 10,
left: 'right',
orient: 'vertical'
}, ['#91cd77','#fc8251','#ef6567'],this.pj)
})
},
// 楼层负责人情况
floorStats () {
const startDate = this.dateRange4L[0].format('YYYY-MM-DD');
const endDate = this.dateRange4L[1].format('YYYY-MM-DD');
floorStats({startDate: startDate, endDate: endDate}).then(res => {
this.createEchartLine('echart4', res.data)
})
},
//故障统计
deviceStats () {
const startDate = this.dateRange4R1[0].format('YYYY-MM-DD');
const endDate = this.dateRange4R1[1].format('YYYY-MM-DD');
deviceStats({ startDate: startDate, endDate: endDate }).then(res => {
this.faultStatsCount = res.data
this.createColumnEcharrt('echart5', res.data)
})
},
//第四行 右下 故障类型统计
failureStats () {
const startDate = this.dateRange4R2[0].format('YYYY-MM-DD');
const endDate = this.dateRange4R2[1].format('YYYY-MM-DD');
failureStats({ startDate: startDate, endDate: endDate }).then(res => {
this.selectFaultStatsCount = res.data
this.createEchartCake('echart6', {
bottom: 10,
left: 'right',
orient: 'vertical'
},['#ef6567', '#fc8251', '#f9c956', '#91cd77'],this.selectFaultStatsCount)
})
},
//第五行 设备、品牌、评价 统计
deviceTypeStats () {
const startDate = this.dateRange5[0].format('YYYY-MM-DD');
const endDate = this.dateRange5[1].format('YYYY-MM-DD');
deviceTypeStats({ startDate: startDate, endDate: endDate}).then(res => {
this.loadData = res.rows
})
},
createEchartCake(id, legend, colors, data) {
//console.log(data)
// 基于准备好的dom初始化echarts实例
let myChart = echarts.init(document.getElementById(id))
// 指定图表的配置项和数据
let option = {
tooltip: {
trigger: 'item'
},
legend: legend,
color: colors,
series: [
{
name: '完成率',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 2,
borderColor: '#fff',
borderWidth: 2
},
label: {
show: true,
position: 'inner',
formatter: '{d}%'
},
emphasis: {
label: {
show: true,
fontSize: 40,
fontWeight: 'bold'
}
},
labelLine: {
show: false
},
data: data
}
]
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
}
,
createEchartLine (id, data) {
// 基于准备好的dom初始化echarts实例
let myChart = echarts.init(document.getElementById(id))
let option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
legend: {},
color: ['#75bedc','#f9c956'],
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: data.y
},
series: [
{
name: '故障数量',
type: 'bar',
barWidth: '5px',
data: data.total
},
{
name: '已完成数量',
type: 'bar',
barWidth: '5px',
data: data.finish
}
]
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
}
,
createColumnEcharrt (id, data) {
data.x = data.x.map(item => {
if (item && item.length > 4) {
item = item.substring(0, 4) + '..'
}
return item
})
// 基于准备好的dom初始化echarts实例
let myChart = echarts.init(document.getElementById(id))
// There should not be negative values in rawData
const rawData = [
data.total,
data.finish
]
//计算,总数-已完成=未完成
for (let i = 0; i < rawData[0].length; ++i) {
rawData[0][i]-=rawData[1][i];
}
const grid = {
left: 100,
right: 100,
top: 50,
bottom: 50
}
const series = [
'未完成',
'已完成'
].map((name, sid) => {
return {
name,
type: 'bar',
stack: 'total',
barWidth: '10px',
label: {
show: true,
formatter: (params) => params.value>0 ? params.value : ""
},
data: rawData[sid].map((d, did) => rawData[sid][did])
}
})
let option = {
legend: {
selectedMode: false
},
color: ['#f9c956','#75bedc'],
grid,
yAxis: {
type: 'value'
},
xAxis: {
type: 'category',
data: data.x
},
series
}
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option)
}
}
}
</script>
<style scoped>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
word-break: break-all;
line-height: 1;
}
.topHeadView {
display: flex;
justify-content: space-around;
align-items: center;
padding: 40px 0;
background: #4093f7;
border-radius: 10px;
}
.topHeadView .itemView {
position: relative;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.topHeadView .itemView .imgView {
width: 80px;
height: 80px;
border-radius: 80px;
background: white;
}
.topHeadView .itemView .imgView {
width: 80px;
height: 80px;
border-radius: 80px;
background: white;
}
.topHeadView .itemView .label {
margin-top: 10px;
color: white;
}
.topHeadView .itemView .num {
position: absolute;
left: calc(100% + 10px);
top: 50%;
transform: translateY(-100%);
width: 90px;
color: white;
font-size: 28px;
}
.caseStatisticsView {
margin-top: 10px;
padding: 0 20px;
border-radius: 10px;
background: white;
}
.caseStatisticsView .headView {
display: flex;
justify-content: space-between;
align-items: center;
}
.caseStatisticsView .headView .titleView {
font-size: 14px;
color: #4093f7;
font-weight: bold;
padding: 14px 30px;
border-bottom: 2px solid #4093f7;
}
.caseStatisticsView .headView .typeView {
display: flex;
justify-content: center;
align-items: center;
}
.caseStatisticsView .headView .typeView .type {
margin-right: 40px;
font-weight: bold;
}
.caseStatisticsView .headView .typeView .activity {
color: #4093f7;
}
.caseStatisticsView .contentView {
display: flex;
justify-content: center;
align-items: center;
padding: 30px 50px;
}
.caseStatisticsView .contentView .itemView {
flex: 1;
border: 1px solid #4093f7;
border-radius: 4px;
padding: 14px 20px;
margin-left: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.caseStatisticsView .contentView .itemView:first-of-type {
margin-left: 0;
}
.caseStatisticsView .contentView .itemView .label {
font-size: 12px;
font-weight: bold;
color: #4093f7;
}
.caseStatisticsView .contentView .itemView .value {
font-size: 12px;
}
.statusView {
display: flex;
justify-content: flex-start;
align-items: center;
margin-top: 10px;
}
.statusView .caseStatusView, .statusView .evaluateView {
background: white;
border-radius: 10px;
}
.statusView .caseStatusView {
flex: 2;
}
.statusView .evaluateView {
flex: 1;
margin-left: 6px;
}
.statusView .caseStatusView .headView, .statusView .evaluateView .headView {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 40px;
}
.statusView .caseStatusView .headView .titleView, .statusView .evaluateView .headView .titleView {
font-size: 14px;
color: gray;
}
.statusView .caseStatusView .headView .typeView {
display: flex;
justify-content: center;
align-items: center;
}
.statusView .caseStatusView .headView .typeView .type {
font-size: 12px;
color: #4093f7;
margin-left: 20px;
padding: 4px 8px;
border-radius: 4px;
}
.statusView .caseStatusView .headView .typeView .type.activity {
border: 1px solid #4093f7;
}
.statusView .caseStatusView .contentView {
display: flex;
justify-content: flex-start;
align-items: center;
}
.statusView .caseStatusView .contentView .circularView {
flex: 1;
display: flex;
justify-content: center;
align-items: center;
}
.statusView .caseStatusView .contentView .centerLine {
width: 1px;
height: 200px;
background: rgba(0, 0, 0, 0.1);
}
.statusView .evaluateView .contentView {
display: flex;
justify-content: center;
align-items: center;
}
.faultView {
display: flex;
justify-content: flex-start;
align-items: flex-start;
margin-top: 10px;
}
.faultView .floorView {
background: white;
border-radius: 10px;
width: 600px;
}
.faultView .floorView .headView {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 40px;
}
.faultView .floorView .headView .titleView {
font-size: 14px;
color: gray;
}
.faultView .floorView .headView .conditionView {
display: flex;
justify-content: center;
align-items: center;
}
.faultView .floorView .contentView {
display: flex;
justify-content: center;
align-items: center;
height: 700px;
}
.rightView {
flex: 1;
margin-left: 6px;
}
.rightView .blockView {
background: white;
border-radius: 10px;
}
.rightView .blockView:last-of-type {
margin-top: 6px;
}
.rightView .blockView .headView {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 40px;
}
.rightView .blockView .headView .titleView {
font-size: 14px;
color: gray;
}
.rightView .blockView .headView .conditionView {
display: flex;
justify-content: center;
align-items: center;
}
.rightView .blockView .contentView {
display: flex;
justify-content: center;
align-items: center;
}
.tableView {
background: white;
border-radius: 10px;
margin-top: 6px;
}
.tableView .headView {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 40px;
}
.tableView .headView .titleView {
font-size: 14px;
color: gray;
}
.tableView .headView .conditionView {
display: flex;
justify-content: center;
align-items: center;
}
.tableView .contentView {
display: flex;
justify-content: center;
align-items: center;
}
</style>