Files
fengjie-datascreen/src/views/workOrder/components/box-2.vue
2025-01-20 04:09:56 +08:00

455 lines
13 KiB
Vue

<template>
<div class="work-box-2">
<div class="box-1">
<div>
<Title1 title="今日工单" />
</div>
<div class="hd-list">
<img class="h-icon" src="@/assets/images/work-icon-1.png" />
<div class="item item1">
今日工单总条数 <span class="color1"><countup :end-val="totalData.total" /></span>
</div>
<div class="item item1">
工单完成数 <span class="color1"><countup :end-val="totalData.complete" /></span>
</div>
<div class="item item3">
紧急工单数 <span class=""><countup :end-val="totalData.warn" /></span>
</div>
<div class="item item2">
重要工单数 <span class=""><countup :end-val="totalData.important" /></span>
</div>
<div class="item item1">
普通工单数 <span class=""><countup :end-val="totalData.normal" /></span>
</div>
</div>
<div class="chart">
<div class="chart__wrapper mr-8">
<Title3 title="工单总数" />
<Line :width="680" :height="320" :data="seriesData" :xAxisData="xAxisData" />
</div>
<div class="chart__wrapper">
<Title3 title="工单类型完成比例" />
<div class="progress">
<div class="progress-item">
<span class="progress-item__label">普通</span>
<div class="progress-item__inner">
<el-progress
:percentage="completeRateData.normal * 100"
:stroke-width="fitChartSize(24)"
stroke-linecap="square"
color="#2380FB"
/>
</div>
</div>
<div class="progress-item">
<span class="progress-item__label">紧急</span>
<div class="progress-item__inner">
<el-progress
:percentage="completeRateData.warn * 100"
:stroke-width="fitChartSize(24)"
stroke-linecap="square"
color="#D9011B"
/>
</div>
</div>
<div class="progress-item">
<span class="progress-item__label">重要</span>
<div class="progress-item__inner">
<el-progress
:percentage="completeRateData.important * 100"
:stroke-width="fitChartSize(24)"
stroke-linecap="square"
color="#FEAE00"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="box-2 mt-8">
<Title1 title="不同景区工单占比" />
<div class="chart">
<div class="chart__wrapper">
<Title3 title="景区工单占比" />
<div class="chart__inner">
<spotRate :dataList="spotRateData" :total="spotRateTotal" label="工单总数" />
<ul class="chart__legend">
<li class="chart__legend-item" v-for="(item, index) in spotRateData" :key="index">
<p class="dot" :style="{ background: colors[index] }" />
<p class="name">{{ item.name }}</p>
<p class="value">{{ item.value }}%</p>
</li>
</ul>
</div>
</div>
<div class="chart__wrapper">
<Title3 title="景区工单类型占比" />
<div class="chart__inner">
<spotRate :dataList="typeRateData" :total="typeRateTotal" label="工单总数" />
<ul class="chart__legend">
<li class="chart__legend-item" v-for="(item, index) in typeRateData" :key="index">
<p class="dot" :style="{ background: colors[index] }" />
<p class="name">{{ item.name }}</p>
<p class="value">{{ item.value }}%</p>
</li>
</ul>
<div v-if="pointRankData.length > 0" class="alarm">
<Title2 title="异常点位告警排名" />
<ul class="alarm__wrapper">
<li class="alarm-item" v-for="(item, index) in pointRankData" :key="index">
<p
class="alarm-item__rank"
:class="{
'alarm-item__rank--error': index == 0,
'alarm-item__rank--warning': index == 1,
'alarm-item__rank--primary': index == 2
}"
>
{{ index + 1 }}
</p>
<p class="alarm-item__content">{{ item.link_title }}</p>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import countup from 'vue-countup-v3'
import spotRate from './spotRate.vue'
import { fitChartSize } from '@/utils/dataUtil'
import {
getTotalApi,
getLineChartApi,
getCompleteRateApi,
getSpotRateApi,
getTypeRateApi
} from '@/api/workOrder'
import { getNewsPointRankApi } from '@/api/news'
const colors = ['#FDC40A', '#FF5232', '#50F0A6', '#5FDFFA']
let totalData = ref({
total: 0,
complete: 0,
urgent: 0,
important: 0,
normal: 0
})
let completeRateData = ref({ important: 0, normal: 0, warn: 0 })
let seriesData = ref([])
let xAxisData = ref([])
let typeRateData = ref([])
let spotRateData = ref([])
let pointRankData = ref([])
let spotRateTotal = ref(0)
let typeRateTotal = ref(0)
const getNewsPointRank = async () => {
let res = await getNewsPointRankApi()
pointRankData.value = res.data
}
const getTypeRate = async () => {
let res = await getTypeRateApi()
typeRateData.value = res.data.data
typeRateTotal.value = res.data.total
}
const getSpotRate = async () => {
let res = await getSpotRateApi()
spotRateData.value = res.data.data
spotRateTotal.value = res.data.total
}
const getCompleteRate = async () => {
let res = await getCompleteRateApi()
completeRateData.value = res.data
}
const getLineChart = async () => {
let res = await getLineChartApi()
seriesData.value = res.data.series
xAxisData.value = res.data.data
}
const getTotal = async () => {
let res = await getTotalApi()
totalData.value = res.data
}
onMounted(() => {
getTotal()
getLineChart()
getCompleteRate()
getTypeRate()
getSpotRate()
getNewsPointRank()
})
</script>
<style lang="scss" scoped>
:deep(.el-progress-bar__outer) {
background: rgba(0, 150, 255, 0.15);
border-radius: 0;
}
:deep(.el-progress-bar__inner) {
border-radius: 0;
}
:deep(.el-progress__text) {
font-size: vw(14) !important;
color: #fff;
}
.chart-p {
position: relative;
.check-label {
position: absolute;
right: vw(20);
top: vh(-15);
span {
min-width: vw(55);
padding: vw(5);
background: linear-gradient(270deg, rgba(8, 41, 86, 0.16) 0%, #0b61b4 100%);
border-radius: vw(50);
margin-left: vw(5);
display: inline-block;
font-weight: 400;
font-size: vw(13);
color: #0084ff;
text-align: center;
font-style: normal;
text-transform: none;
}
.active {
background: linear-gradient(270deg, #37d8fc 0%, #0084ff 100%);
border-radius: vw(50);
border: 1px solid rgba(0, 114, 220, 0.3);
color: #fff;
}
}
}
.work-box-2 {
margin-top: vh(120);
margin-left: vw(8);
.box-1 {
padding: vw(1) 0;
box-sizing: border-box;
background: linear-gradient(to bottom, #0b2f64 0%, #062b57 100%);
.chart {
display: flex;
justify-content: space-between;
padding: vw(20) vw(15);
&__wrapper {
width: vw(740);
height: vh(370);
padding: 0 vw(20);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
.progress {
padding: vw(30);
padding-top: vh(40);
&-item {
display: flex;
width: 100%;
margin-top: vh(40);
align-items: center;
}
&-item__label {
font-weight: bold;
font-size: vw(14);
color: #ffffff;
text-align: left;
font-style: normal;
text-transform: none;
margin-right: vw(10);
}
&-item__inner {
flex: 1;
}
}
}
}
.hd-list {
display: flex;
justify-content: center;
position: relative;
.h-icon {
position: absolute;
left: vw(70);
top: 50%;
transform: translateY(-50%);
width: vw(74);
height: auto;
}
.item {
display: flex;
width: vw(210);
height: vh(58);
line-height: vh(58);
padding-left: vw(10);
text-align: center;
margin: 0 vw(15);
font-weight: 400;
font-size: vw(14);
color: rgba(255, 255, 255, 0.9);
text-align: left;
font-style: normal;
text-transform: none;
span {
font-size: vw(24);
position: relative;
top: vh(2);
margin-left: vw(5);
}
.color1 {
color: #02f9fa;
}
}
.item1 {
background-image: url('@/assets/images/work-n-bg-1.png');
background-size: 100% 100%;
}
.item2 {
background-image: url('@/assets/images/work-n-bg-2.png');
background-size: 100% 100%;
}
.item3 {
background-image: url('@/assets/images/work-n-bg-3.png');
background-size: 100% 100%;
}
}
}
.box-2 {
padding: vw(1);
height: vh(442);
box-sizing: border-box;
background: linear-gradient(321deg, #0b2f64 0%, #062b57 100%);
.chart {
display: flex;
justify-content: space-between;
padding: vw(20) vw(15);
&__wrapper {
width: vw(740);
height: vh(370);
padding: 0 vw(20);
background-image: url('@/assets/images/bg-3.png');
background-size: 100% 100%;
}
&__inner {
display: flex;
align-items: center;
}
&__legend {
flex: 1;
&-item {
position: relative;
width: 100%;
height: vh(40);
display: flex;
align-items: center;
margin-bottom: vh(8);
background: linear-gradient(
90deg,
rgba(0, 150, 255, 0.34) 0%,
rgba(0, 150, 255, 0) 100%
);
&::before {
position: absolute;
content: '';
width: vw(4);
height: vh(40);
background-color: #0096ff;
}
.dot {
width: vw(10);
height: vw(10);
margin: 0 vw(16);
}
.name {
font-weight: 400;
font-size: vw(12);
color: #ffffff;
width: vw(130);
}
.value {
font-weight: bold;
font-size: vw(15);
color: #ffffff;
}
}
}
}
.alarm {
width: vw(200);
background: #0a4190;
&__wrapper {
width: vw(200);
height: vh(270);
background: #054581; /* 滚动条整体样式 */
&::-webkit-scrollbar {
width: vw(4); /* 滚动条的宽度 */
}
/* 滚动条轨道 */
&::-webkit-scrollbar-track {
background: 'transparent'; /* 轨道的背景色 */
}
/* 滚动条滑块 */
&::-webkit-scrollbar-thumb {
background: rgba(0, 150, 255, 0.63); /* 滑块的背景色 */
border-radius: 5px; /* 滑块的圆角 */
}
overflow: auto;
}
&-item {
padding: 0 vw(12);
&:nth-child(2n) {
background: #054d8d;
}
height: vh(40);
display: flex;
align-items: center;
}
&-item__rank {
width: vw(24);
height: vh(16);
font-size: vw(12);
color: #ffffff;
display: flex;
align-items: center;
justify-content: center;
background: #495c77;
}
&-item__rank--error {
background-color: #d9011b;
}
&-item__rank--warning {
background-color: #feae00;
}
&-item__rank--primary {
background-color: #2380fb;
}
&-item__content {
padding-left: vw(20);
font-weight: 400;
font-size: vw(14);
color: #ffffff;
}
}
}
}
</style>