bonus-ui/src/views/EquipmentLedger/components/EquAnalysis.vue

461 lines
12 KiB
Vue
Raw Normal View History

2026-02-02 15:14:06 +08:00
<template>
<div class="warp">
<div class="item df">
<div>
<div class="title">
<div class="title-text">装备台账分析</div>
<div class="tab" :class="{ active: activeTab1 == 1 }" @click="handleActiveTab(1)">总价值</div>
<div class="tab" :class="{ active: activeTab1 == 2 }" @click="handleActiveTab(2)">配置率</div>
</div>
<div v-if="activeTab1 == 1" class="price">
<div class="tab-title">装备资产总价值</div>
<div style="margin-bottom: 35px">
<span class="price-text">{{ priceData.total || 0 }}</span>
<span class="unit-text">万元</span>
</div>
<div class="df" style="justify-content: space-between">
<div>
<div class="tab-title">线路装备</div>
<div class="price-small-text p-df">
<span>{{ priceData.line || 0 }}</span>
<span class="unit-text">万元</span>
</div>
</div>
<el-divider direction="vertical" class="tab-divider" />
<div>
<div class="tab-title">变电装备</div>
<div class="price-small-text p-df">
{{ priceData.substation || 0 }}
<span class="unit-text">万元</span>
</div>
</div>
<el-divider direction="vertical" class="tab-divider" />
<div>
<div class="tab-title">电缆装备</div>
<div class="price-small-text p-df">
{{ priceData.cable || 0 }}
<span class="unit-text">万元</span>
</div>
</div>
</div>
</div>
<div v-else class="config">
<div class="tab-title">装备配置率</div>
<div style="margin-bottom: 35px">
<span class="price-text config-color">{{ configData.total || 0 }}</span>
<span class="unit-text"></span>
</div>
<div class="df" style="justify-content: space-between">
<div>
<div class="tab-title">线路装备</div>
<div class="price-small-text config-color">
{{ configData.line || 0 }}
<span class="unit-text"></span>
</div>
</div>
<el-divider direction="vertical" class="tab-divider" />
<div>
<div class="tab-title">变电装备</div>
<div class="price-small-text config-color">
{{ configData.substation || 0 }}
<span class="unit-text"></span>
</div>
</div>
<el-divider direction="vertical" class="tab-divider" />
<div>
<div class="tab-title">电缆装备</div>
<div class="price-small-text config-color">
{{ configData.cable || 0 }}
<span class="unit-text"></span>
</div>
</div>
</div>
</div>
</div>
<el-divider direction="vertical" class="v-divider" />
<div class="item-right">
<div class="title">
<div class="title-text">装备状态分析</div>
</div>
<div class="chart-wrapper">
<div ref="chart" class="chart"></div>
</div>
</div>
</div>
<el-divider direction="vertical" class="v-divider" />
<div class="item">
<div class="title">
<div class="title-text">装备数量分析</div>
<div class="tab" :class="{ active: activeTab2 == 'major' }" @click="handleActiveTab('major')">三大专业</div>
<div class="tab" :class="{ active: activeTab2 == 'process' }" @click="handleActiveTab('process')">十大工序</div>
</div>
<div ref="barChart" class="barChart"></div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import {
getTotalValueApi,
getConfigurationRateApi,
getEquipmentStatusApi,
getThreeMajorApi,
getTenMajorApi,
} from '@/api/EquipmentEntryApply'
export default {
name: 'EquAnalysis',
props: {
queryParams: {
type: Object,
default: {},
},
},
data() {
return {
priceData: {},
configData: {},
activeTab1: 1,
activeTab2: 'major',
total: 0,
chartData: [
{ name: '在库数量', value: 0 },
{ name: '维修数量', value: 0 },
{ name: '自用数量', value: 0 },
{ name: '共享数量', value: 0 },
{ name: '退役数量', value: 0 },
],
chart: null,
dataMap: {
major: [],
process: [],
},
barChart: null,
}
},
mounted() {},
beforeDestroy() {
window.removeEventListener('resize', this.resizeChart)
this.chart && this.chart.dispose()
window.removeEventListener('resize', this.resizeBarChart)
this.barChart && this.barChart.dispose()
},
methods: {
handleActiveTab(type) {
if (this.activeTab1 == type || this.activeTab2 == type) return
if (type == 1 || type == 2) {
this.activeTab1 = type
} else {
this.activeTab2 = type
this.renderBarChart()
}
},
async init() {
try {
console.log('🚀 ~ this.queryParams:', this.queryParams)
const res = await getTotalValueApi(this.queryParams)
this.priceData = res.data
const res2 = await getConfigurationRateApi(this.queryParams)
this.configData = res2.data
const res3 = await getEquipmentStatusApi(this.queryParams)
this.total = res3.data.total
const fieldMap = [
{ name: '在库数量', key: 'availableNum' },
{ name: '维修数量', key: 'repairNum' },
{ name: '自用数量', key: 'inNum' },
{ name: '共享数量', key: 'shareNum' },
{ name: '退役数量', key: 'scrapNum' },
]
this.chartData = fieldMap.map((item) => ({
name: item.name,
value: res3.data[item.key] || 0,
}))
const res4 = await getThreeMajorApi(this.queryParams)
if (res4.data && res4.data.length > 0) {
this.dataMap.major = res4.data.map((item) => ({
name: item.type,
value: item.total,
}))
}
const res5 = await getTenMajorApi(this.queryParams)
if (res5.data && res5.data.length > 0) {
this.dataMap.process = res5.data.map((item) => ({
name: item.type,
value: item.total,
}))
}
} catch (error) {
console.log('🚀 ~ error-->装备台账分析:', error)
} finally {
this.initChart()
this.initBarChart()
window.addEventListener('resize', this.resizeChart)
window.addEventListener('resize', this.resizeBarChart)
}
},
// 饼图
resizeChart() {
this.chart && this.chart.resize()
},
initChart() {
this.chart = echarts.init(this.$refs.chart)
const option = {
tooltip: {
trigger: 'item',
formatter: (params) => {
const percent = ((params.value / this.total) * 100).toFixed(2)
return `${params.name}<br/>${percent}% / ${params.value}`
},
},
legend: {
orient: 'vertical',
right: '5%',
top: 'center',
itemWidth: 10,
itemHeight: 10,
formatter: (name) => {
const item = this.chartData.find((i) => i.name === name)
const percent = item.value > 0 && this.total > 0 ? ((item.value / this.total) * 100).toFixed(2) : '0.00'
return `${name} ${percent}% / ${item.value}`
},
},
series: [
{
type: 'pie',
radius: ['60%', '80%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
label: { show: false },
labelLine: { show: false },
data: this.chartData,
},
],
graphic: [
{
type: 'text',
left: '27%',
top: '40%',
style: {
text: `${this.total}`,
textAlign: 'center',
fill: '#3F3F3F',
fontSize: 22,
fontWeight: 'bold',
},
},
{
type: 'text',
left: '29%',
top: '53%',
style: {
text: '装备台账',
textAlign: 'center',
fill: '#3F3F3F',
fontSize: 14,
},
},
],
color: ['#33C3C8', '#FF6B6B', '#FFA940', '#FADB14', '#597EF7'],
}
this.chart.setOption(option)
},
// 柱状图
resizeBarChart() {
this.barChart && this.barChart.resize()
},
initBarChart() {
this.barChart = echarts.init(this.$refs.barChart)
this.renderBarChart()
},
renderBarChart() {
const list = this.dataMap[this.activeTab2]
const xData = list.map((i) => i.name)
const yData = list.map((i) => i.value)
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
formatter: (params) => {
const item = params[0]
return `
<div>
<div>${item.name}</div>
<div>数量${item.value}</div>
</div>
`
},
},
grid: {
left: 40,
right: 20,
top: 30,
bottom: 50,
},
xAxis: {
type: 'category',
data: xData,
axisTick: { show: false },
axisLine: { lineStyle: { color: '#E5E5E5' } },
axisLabel: {
color: '#666',
interval: 0,
},
},
yAxis: {
type: 'value',
axisLine: { show: false },
axisTick: { show: false },
splitLine: {
lineStyle: {
type: 'dashed',
color: '#EAEAEA',
},
},
},
series: [
{
type: 'bar',
data: yData,
barWidth: 24,
itemStyle: {
borderRadius: [12, 12, 0, 0],
color: (params) => {
const colors = [
['#5DA9FF', '#8EC5FF'],
['#4ED6C8', '#7BE7D7'],
['#FDBA4F', '#FFD78A'],
]
return new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: colors[params.dataIndex % 3][0] },
{ offset: 1, color: colors[params.dataIndex % 3][1] },
])
},
},
},
],
}
this.barChart.setOption(option, true)
},
},
}
</script>
<style lang="scss" scoped>
.warp {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
.item {
width: 50%;
}
.item-right {
width: 100%;
}
}
.v-divider {
height: 16rem;
}
.df {
display: flex;
}
.title {
display: flex;
align-items: center;
font-size: 16px;
color: #3f3f3f;
line-height: 24px;
margin-bottom: 20px;
.title-text {
font-weight: bold;
}
.tab {
margin-left: 20px;
font-weight: 400;
cursor: pointer;
}
.active {
color: #2cbab2;
border-bottom: 2px solid #2cbab2;
}
}
.price,
.config {
width: 260px;
height: 206px;
padding: 10px;
}
.price {
background: url('~@/assets/images/equ-price.png');
background-size: 100% 100%;
}
.config {
background: url('~@/assets/images/equ-config.png');
background-size: 100% 100%;
}
.tab-title {
font-weight: 400;
font-size: 14px;
color: #3f3f3f;
line-height: 22px;
margin-bottom: 20px;
}
.price-text {
font-family: 'OpenSans-BoldItalic';
font-weight: normal;
font-size: 30px;
color: #ffab29;
line-height: 24px;
}
.price-small-text {
font-family: 'OpenSans-BoldItalic';
font-weight: normal;
font-size: 18px;
color: #ffab29;
line-height: 24px;
}
.p-df {
display: flex;
flex-direction: column;
}
.config-color {
color: #2cbab2;
}
.unit-text {
font-weight: 400;
font-size: 12px;
color: #808080;
line-height: 18px;
}
.tab-divider {
margin-top: 5px;
height: 70px;
background-color: #fff;
}
// 饼图
.chart-wrapper {
width: 80%;
height: 220px;
}
.chart {
width: 100%;
height: 100%;
}
// 柱状图
.barChart {
width: 90%;
height: 220px;
}
</style>