装备台账

This commit is contained in:
bb_pan 2026-02-02 15:14:06 +08:00
parent 4f931ac21f
commit 5d01997d39
7 changed files with 524 additions and 4 deletions

View File

@ -77,4 +77,9 @@ export default {
}
}
@font-face {
font-family: 'OpenSans-BoldItalic';
src: url('~@/assets/font-family/OpenSans-BoldItalic.ttf');
}
</style>

View File

@ -368,3 +368,48 @@ export const getConfigApi = (data) => {
method: 'GET',
})
}
// 总价值
export const getTotalValueApi = (data = {}) => {
return request({
url: `/material-mall/device/getTotalValue`,
method: 'post',
data
})
}
// 配置率
export const getConfigurationRateApi = (data = {}) => {
return request({
url: `/material-mall/device/getConfigurationRate`,
method: 'post',
data
})
}
// 装备状态分析
export const getEquipmentStatusApi = (data = {}) => {
return request({
url: `/material-mall/device/getEquipmentStatus`,
method: 'post',
data
})
}
// 装备数量分析-三大专业
export const getThreeMajorApi = (data = {}) => {
return request({
url: `/material-mall/device/getThreeMajor`,
method: 'post',
data
})
}
// 装备数量分析-十大工序
export const getTenMajorApi = (data = {}) => {
return request({
url: `/material-mall/device/getTenMajor`,
method: 'post',
data
})
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

View File

@ -0,0 +1,460 @@
<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>

View File

@ -71,9 +71,8 @@
</el-col>
<!-- 右侧列表和查询区域 -->
<el-col :span="collapsed ? 23 : 20" style="height: calc(100vh - 130px); transition: all 0.3s ease">
<div class="card-container">
<!-- <div class="card-container">
<div class="card-header">
<!-- 搜索区域展开/收起按钮 -->
<div class="status-stats">
<div class="stat-card">
<div class="stat-content">
@ -133,7 +132,7 @@
</div>
</div>
</div> -->
<div class="status-operation-bar" style="margin-top: 15px;margin-bottom: 2px;">
<el-form
@ -360,6 +359,11 @@
</div>
</div>
<div class="status-operation-bar" style="margin: 15px 0">
<!-- 分析 -->
<EquAnalysis ref="equAnalysis" :queryParams="queryParams" />
</div>
<!-- 装备列表表格 -->
<div class="card-container content-box">
<el-row :gutter="10" class="mb8" style="display: flex; align-items: center;border-bottom: 1px solid #e4e7ed;">
@ -903,11 +907,12 @@ import { firstLevel, secondAndThirdLevel } from '@/api/EquipmentEntryApply'
import { getMaxFeatureAPI, getProvinceListAPI } from '@/api/EquipmentLedger/equ-out.js'
import { deptTreeSelect } from '@/api/system/user'
import AddEquip from '@/views/stockManagement/entryApply/components/AddEquip'
import EquAnalysis from '@/views/EquipmentLedger/components/EquAnalysis'
export default {
name: 'EquipmentLedger',
dicts: ['user_year_type'],
components: { AddEquip, QrcodeGenerator },
components: { AddEquip, QrcodeGenerator, EquAnalysis },
data() {
return {
treeSearchKey: '', //
@ -1207,6 +1212,9 @@ export default {
toggleCollapse() {
this.collapsed = !this.collapsed
// /
this.$nextTick(() => {
this.$refs.equAnalysis.init()
})
},
/**
* 切换搜索区域展开/收起状态
@ -1547,6 +1555,8 @@ export default {
this.tableData = listResult.data?.rows || []
this.total = listResult.data?.total || 0
this.$refs.equAnalysis.init()
let maxLength = 0
this.tableData.forEach((item) => {
if (item.propertyVoList && item.propertyVoList.length > maxLength) {