装备配置率配置

This commit is contained in:
jiang 2026-01-26 19:49:32 +08:00
parent b7597e8f8a
commit 3e78f2c738
3 changed files with 1143 additions and 265 deletions

View File

@ -10,6 +10,30 @@ export function listUser(query) {
})
}
export function getOwnEquipmentList(query) {
return request({
url: '/material-mall/deptConfig/getOwnEquipmentList',
method: 'get',
params: query
})
}
export function getSharingEquipmentList(query) {
return request({
url: '/material-mall/deptConfig/getSharingEquipmentList',
method: 'get',
params: query
})
}
export function getRentalEquipmentList(query) {
return request({
url: '/material-mall/deptConfig/getRentalEquipmentList',
method: 'get',
params: query
})
}
export function selectResourceList(query) {
return request({
url: '/material-mall/deptConfig/selectResourceList',
@ -26,6 +50,41 @@ export function selectInventoryList(query) {
})
}
export function saveInstanceSelection(data) {
return request({
url: '/material-mall/deptConfig/saveInstanceSelection',
method: 'post',
data: data
})
}
export function getConfigurationTotal() {
return request({
url: '/material-mall/deptConfig/getConfigurationTotal',
method: 'post'
})
}
export function getConfigurationDetails(data) {
return request({
url: '/material-mall/deptConfig/getConfigurationDetails',
method: 'post',
data: data
})
}
export function exportStatsData() {
return request({
url: '/material-mall/deptConfig/exportStatsData',
method: 'get',
})
}
export function insertResource(data) {
return request({
url: '/material-mall/deptConfig/insertResource',

View File

@ -1,227 +1,389 @@
<template>
<div class="equipment-table">
<h2 class="title" v-if="!showSecondPage">机械化机械化施工装备配置率 </h2>
<el-card class="content-box" style="height: 800px" v-if="!showSecondPage">
<div v-if="!showSecondPage">
<el-table
:data="tableData"
border
stripe
:header-cell-style="headerCellStyle"
show-overflow-tooltip
:cell-style="{ textAlign: 'center' }"
class="custom-table"
height="800"
>
<el-table-column width="240" align="center">
<template slot="header">
<div class="diagonal-header">
<div class="diagonal-line"></div>
<div class="header-top">指标项</div>
<div class="header-bottom">公司</div>
</div>
</template>
<template slot-scope="scope">
<div>{{ scope.row.companyName }}</div>
</template>
</el-table-column>
<el-table-column prop="valueA" label="线路设备配置率">
<template slot-scope="scope">
<span class="link" @click="handleDrill(scope.row, 0)">{{ scope.row.valueA + scope.row.newValueA }}</span>
</template>
</el-table-column>
<el-table-column prop="valueB" label="电缆设备配置率">
<template slot-scope="scope">
<span class="link" @click="handleDrill(scope.row, 1)">{{ scope.row.valueB+scope.row.newValueB }}</span>
</template>
</el-table-column>
<el-table-column prop="valueC" label="变电设备配置率">
<template slot-scope="scope">
<span class="link" @click="handleDrill(scope.row, 2)">{{ scope.row.valueC+scope.row.newValueC }}</span>
</template>
</el-table-column>
</el-table>
<div class="app-container">
<!-- 图表卡片纯div布局保留核心ref和样式类 -->
<div class="chart-card">
<div class="chart-container" ref="chartContainer">
<div ref="chartRef" class="echarts echarts-box"></div>
</div>
</el-card>
</div>
<LineDetail
v-if="showSecondPage"
:key="secondPageTitleId + '_' + secondPageTitle"
:title="secondPageTitle"
:secondPageTitleId="secondPageTitleId"
:table-data="detailTableData"
@back="handleBack"
/>
<div class="table-card">
<div class="table-header">
<el-button
type="primary"
style="float: right"
icon="el-icon-download"
@click="exportTableData"
>
导出数据
</el-button>
</div>
<el-table
:data="tableData"
border
stripe
:fit="true"
class="stats-table"
v-loading="tableLoading"
>
<el-table-column label="序号" type="index" width="60" align="center"/>
<el-table-column label="公司" prop="deptName" min-width="150" align="center"/>
<el-table-column
label="线路设备配置率"
prop="lineNum"
min-width="120"
align="center"
>
<template slot-scope="scope">
<span class="link" @click="handleCellClick(scope.row.deptId, 0)">{{ scope.row.lineNum }}</span>
</template>
</el-table-column>
<el-table-column
label="电缆设备配置率"
prop="cableNum"
min-width="120"
align="center"
>
<template slot-scope="scope">
<span class="link" @click="handleCellClick(scope.row.deptId, 2)">{{ scope.row.cableNum }}</span>
</template>
</el-table-column>
<el-table-column
label="变电设备配置率"
prop="substationNum"
min-width="120"
align="center"
>
<template slot-scope="scope">
<span class="link" @click="handleCellClick(scope.row.deptId, 1)">{{ scope.row.substationNum }}</span>
</template>
</el-table-column>
<el-table-column label="总配置率" prop="num" min-width="100" align="center"/>
</el-table>
</div>
<!-- 配置率详情弹窗 -->
<el-dialog
title="装备配置率明细"
:visible.sync="detailDialogVisible"
width="90%"
top="20px"
:close-on-click-modal="false"
>
<el-table
:data="detailTableData"
border
stripe
v-loading="detailTableLoading"
max-height="600px"
>
<!-- 明细表格列匹配用户提供的表格结构 -->
<el-table-column label="序号" type="index" width="60" align="center"/>
<el-table-column label="装备名称" prop="typeName" min-width="120" align="center"/>
<el-table-column label="电压等级" prop="vol" min-width="100" align="center"/>
<el-table-column label="地理特征" prop="type" min-width="100" align="center"/>
<el-table-column label="配置标准(台)" prop="configValue" min-width="100" align="center"/>
<el-table-column label="装备种类" prop="jijuType" min-width="100" align="center"/>
<el-table-column label="配置说明" prop="configDescription" min-width="120" align="center"/>
<el-table-column label="装备配置率赋值" prop="configRate" min-width="120" align="center"/>
<el-table-column label="自有装配数量" prop="ownNum" min-width="120" align="center"/>
<el-table-column label="共享装备数量" prop="shareNum" min-width="120" align="center"/>
<el-table-column label="外租装备数量" prop="rentOutNum" min-width="120" align="center"/>
<el-table-column label="装备实际配置率" prop="baseAddNum" min-width="120" align="center"/>
<el-table-column label="特殊装备配备原因" prop="remark" min-width="150" align="center"/>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button @click="detailDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { listRate, getDetailList } from '@/api/rent-facility/index'
import LineDetail from '@/views/rent-facility/line.vue'
import * as echarts from 'echarts'
import { getConfigurationTotal, exportStatsData, getConfigurationDetails } from '@/api/system/equipmentNew'
export default {
components: { LineDetail },
name: 'ConfigStatsPage',
data() {
return {
//
showSearch: true,
searchForm: {
companyName: '',
voltageLevel: '',
terrainType: ''
},
tableData: [],
showSecondPage: false,
secondPageTitle: '',
secondPageTitleId: '',
detailTableData: []
}
},
computed: {
//
headerCellStyle() {
return {
background: '#2CBAB2 !important',
color: '#fff',
textAlign: 'center'
}
tableLoading: false,
tableHeight: 'auto',
chartInstance: null,
chartOption: {
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
formatter: function(params) {
let res = params[0].name
params.forEach((item) => {
const nameMap = {
xianlu: '线路配置率',
dianlan: '电缆配置率',
biandian: '变电配置率'
}
res += `<br/>${nameMap[item.seriesName] || item.seriesName}${item.value}`
})
return res
}
},
legend: {
data: ['xianlu', 'dianlan', 'biandian'],
formatter: function(name) {
const nameMap = { xianlu: '线路', dianlan: '电缆', biandian: '变电' }
return nameMap[name] || name
},
right: '10%'
},
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: {
type: 'category',
data: [],
axisLabel: {
rotate: 0,
fontSize: 12,
interval: 0,
width: 80,
lineHeight: 20,
align: 'center',
formatter: function(value) {
let str = ''
for (let i = 0; i < value.length; i++) {
str += value[i]
if ((i + 1) % 6 === 0) str += '\n'
}
return str
}
}
},
yAxis: { type: 'value', axisLabel: { formatter: '{value}' } },
series: [
{ name: 'xianlu', type: 'bar', data: [], itemStyle: { color: '#409EFF' } },
{ name: 'dianlan', type: 'bar', data: [], itemStyle: { color: '#67C23A' } },
{ name: 'biandian', type: 'bar', data: [], itemStyle: { color: '#E6A23C' } }
]
},
resizeTimer: null,
//
detailDialogVisible: false,
detailTableData: [],
detailTableLoading: false,
//
currentClickRow: null,
currentClickType: '' // line(线)/cable()/substation()
}
},
mounted() {
this.fetchData()
this.$nextTick(async() => {
await this.initPage()
})
},
methods: {
//
async fetchData() {
//
async initPage() {
this.tableLoading = true
try {
const response = await listRate()
if (response.code === 200 && response.rows) {
this.tableData = response.rows || []
} else {
console.error('API返回数据格式错误:', response)
const res = await getConfigurationTotal(this.searchForm)
if (res.code === 200) {
const statsData = res.data || []
this.tableData = statsData
this.$nextTick(() => {
this.initChart()
this.updateChartData(statsData)
})
}
} catch (error) {
console.error('获取数据失败:', error)
} catch (err) {
console.error('获取统计数据失败:', err)
this.$message.error('获取统计数据失败,请稍后重试')
} finally {
this.tableLoading = false
}
},
//
async handleDrill(row, type) {
// 1.
this.showSecondPage = true
this.detailTableData = []
// EChartsDOM+
initChart() {
const chartDom = this.$refs.chartRef
if (!chartDom || this.chartInstance) return
// 2.
const res = await getDetailList({ companyId: row.companyId, configType: type })
if (res.code === 200) {
this.secondPageTitle = `${row.companyName}详情页面`
this.secondPageTitleId = row.companyId
this.detailTableData = res.rows
const clientWidth = chartDom.clientWidth || 800
const clientHeight = chartDom.clientHeight || 400
if (clientWidth === 0 || clientHeight === 0) {
chartDom.style.width = '100%'
chartDom.style.height = '450px'
}
// 3.
this.$nextTick(() => {
this.showSecondPage = true
})
if (this.chartInstance) {
this.chartInstance.dispose()
this.chartInstance = null
}
this.chartInstance = echarts.init(chartDom)
this.chartInstance.setOption(this.chartOption, true)
},
//
updateChartData(data) {
if (!this.chartInstance || !Array.isArray(data) || data.length === 0) return
const xAxisData = []
const xianluData = []
const dianlanData = []
const biandianData = []
data.forEach((item) => {
xAxisData.push(item.deptName || '')
xianluData.push(item.lineNum || 0)
dianlanData.push(item.cableNum || 0)
biandianData.push(item.substationNum || 0)
})
const allData = [...xianluData, ...dianlanData, ...biandianData]
const maxValue = Math.max(...allData)
const dynamicYmax = Math.ceil(maxValue * 1.1)
this.chartInstance.setOption({
xAxis: { data: xAxisData },
yAxis: { max: dynamicYmax },
series: [
{ name: 'xianlu', data: xianluData },
{ name: 'dianlan', data: dianlanData },
{ name: 'biandian', data: biandianData }
]
})
this.chartInstance.resize()
},
// loading
async exportTableData() {
this.$loading({ text: '正在导出,请稍候...' })
try {
let fileName = `装备配置率统计_${new Date().getTime()}.xLsx`
let url = '/material-mall/deptConfig/exportStatsData'
this.download(url, {}, fileName)
this.$message.success('导出成功')
} catch (err) {
console.error('导出失败:', err)
this.$message.error('导出失败,请稍后重试')
} finally {
this.$loading().close()
}
},
//
handleBack() {
this.showSecondPage = false
//
handleCellClick(deptId, configType) {
//
this.detailDialogVisible = true
this.loadConfigDetail(deptId, configType)
},
//
async loadConfigDetail(deptId, configType) {
this.detailTableLoading = true
try {
// ++
const params = {
deptId: deptId,
configType: configType
}
const res = await getConfigurationDetails(params)
if (res.code === 200) {
this.detailTableData = res.data || []
}
} catch (err) {
console.error('获取配置率明细失败:', err)
this.$message.error('获取配置率明细失败,请稍后重试')
this.detailTableData = []
} finally {
this.detailTableLoading = false
}
}
}
}
</script>
<style lang="scss" scoped>
.equipment-table {
padding: 20px;
background: #fff;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
<!-- 非scoped样式ECharts容器+全局滚动条保留 -->
<style lang="scss">
.echarts-box {
width: 100% !important;
height: 100% !important;
min-width: 300px;
min-height: 300px;
}
.title {
text-align: center;
font-weight: bold;
font-size: 18px;
margin-bottom: 20px;
color: #2CBAB2;
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.diagonal-header {
::-webkit-scrollbar-thumb {
border-radius: 3px;
background: #dcdfe6;
}
::-webkit-scrollbar-track {
background: #f5f7fa;
}
</style>
<!-- scoped样式核心调整适配纯div布局 -->
<style scoped lang="scss">
.app-container {
width: 100%;
// background-color: #00a288;
// color: white;
position: relative;
height: 40px;
font-size: 12px;
height: calc(99vh - 84px);
padding: 16px;
box-sizing: border-box;
background: #f5f7fa;
overflow: auto;
display: flex;
flex-direction: column;
gap: 16px;
}
.diagonal-line {
position: absolute;
top: 0;
left: 0;
.chart-card {
background: #fff;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
padding: 16px;
box-sizing: border-box;
width: 100%;
height: 450px;
flex: 0 0 auto;
}
.chart-container {
width: 100%;
height: 100%;
background: linear-gradient(
to top right,
transparent 49.5%,
white 49.5%,
white 50.5%,
transparent 50.5%
);
}
.header-top {
position: absolute;
top: 4px;
right: 6px;
z-index: 1;
}
.header-bottom {
position: absolute;
bottom: 4px;
left: 8px;
z-index: 1;
.table-card {
background: #fff;
border-radius: 4px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
padding: 16px;
height: 99vh;
box-sizing: border-box;
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
}
.link {
color: #2CBAB2 ;
color: #2CBAB2;
cursor: pointer;
text-decoration: underline;
}
.link:hover {
color: #23b8b1;
}
.custom-table :deep(th),
.custom-table :deep(td) {
border: 1px solid #ebeef5;
text-align: center;
}
::v-deep .el-table {
//
&.el-table--striped .el-table__body {
tr.el-table__row--striped td {
background-color: #F6FBFA !important; //
}
}
.el-table__header {
background: #E9F0EE;
th {
background: #E9F0EE !important;
color: #606266;
font-weight: 600;
height: 50px;
}
}
&.el-table--striped .el-table__body tr.el-table__row:hover > td.el-table__cell {
background-color: #CCF1E9 !important;
}
}
</style>

File diff suppressed because it is too large Load Diff