装备配置率配置

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) { export function selectResourceList(query) {
return request({ return request({
url: '/material-mall/deptConfig/selectResourceList', 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) { export function insertResource(data) {
return request({ return request({
url: '/material-mall/deptConfig/insertResource', url: '/material-mall/deptConfig/insertResource',

View File

@ -1,227 +1,389 @@
<template> <template>
<div class="equipment-table"> <div class="app-container">
<h2 class="title" v-if="!showSecondPage">机械化机械化施工装备配置率 </h2> <!-- 图表卡片纯div布局保留核心ref和样式类 -->
<el-card class="content-box" style="height: 800px" v-if="!showSecondPage"> <div class="chart-card">
<div v-if="!showSecondPage"> <div class="chart-container" ref="chartContainer">
<el-table <div ref="chartRef" class="echarts echarts-box"></div>
: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> </div>
</el-card> </div>
<LineDetail <div class="table-card">
v-if="showSecondPage" <div class="table-header">
:key="secondPageTitleId + '_' + secondPageTitle" <el-button
:title="secondPageTitle" type="primary"
:secondPageTitleId="secondPageTitleId" style="float: right"
:table-data="detailTableData" icon="el-icon-download"
@back="handleBack" @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> </div>
</template> </template>
<script> <script>
import { listRate, getDetailList } from '@/api/rent-facility/index' import * as echarts from 'echarts'
import LineDetail from '@/views/rent-facility/line.vue' import { getConfigurationTotal, exportStatsData, getConfigurationDetails } from '@/api/system/equipmentNew'
export default { export default {
components: { LineDetail }, name: 'ConfigStatsPage',
data() { data() {
return { return {
// showSearch: true,
searchForm: {
companyName: '',
voltageLevel: '',
terrainType: ''
},
tableData: [], tableData: [],
showSecondPage: false, tableLoading: false,
secondPageTitle: '', tableHeight: 'auto',
secondPageTitleId: '', chartInstance: null,
detailTableData: [] chartOption: {
} tooltip: {
}, trigger: 'axis',
computed: { axisPointer: { type: 'shadow' },
// formatter: function(params) {
headerCellStyle() { let res = params[0].name
return { params.forEach((item) => {
background: '#2CBAB2 !important', const nameMap = {
color: '#fff', xianlu: '线路配置率',
textAlign: 'center' 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() { mounted() {
this.fetchData() this.$nextTick(async() => {
await this.initPage()
})
}, },
methods: { methods: {
// //
async fetchData() { async initPage() {
this.tableLoading = true
try { try {
const response = await listRate() const res = await getConfigurationTotal(this.searchForm)
if (response.code === 200 && response.rows) { if (res.code === 200) {
this.tableData = response.rows || [] const statsData = res.data || []
} else { this.tableData = statsData
console.error('API返回数据格式错误:', response) this.$nextTick(() => {
this.initChart()
this.updateChartData(statsData)
})
} }
} catch (error) { } catch (err) {
console.error('获取数据失败:', error) console.error('获取统计数据失败:', err)
this.$message.error('获取统计数据失败,请稍后重试')
} finally {
this.tableLoading = false
} }
}, },
// // EChartsDOM+
async handleDrill(row, type) { initChart() {
// 1. const chartDom = this.$refs.chartRef
this.showSecondPage = true if (!chartDom || this.chartInstance) return
this.detailTableData = []
// 2. const clientWidth = chartDom.clientWidth || 800
const res = await getDetailList({ companyId: row.companyId, configType: type }) const clientHeight = chartDom.clientHeight || 400
if (res.code === 200) { if (clientWidth === 0 || clientHeight === 0) {
this.secondPageTitle = `${row.companyName}详情页面` chartDom.style.width = '100%'
this.secondPageTitleId = row.companyId chartDom.style.height = '450px'
this.detailTableData = res.rows }
// 3. if (this.chartInstance) {
this.$nextTick(() => { this.chartInstance.dispose()
this.showSecondPage = true 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() { handleCellClick(deptId, configType) {
this.showSecondPage = false //
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> </script>
<style lang="scss" scoped> <!-- 非scoped样式ECharts容器+全局滚动条保留 -->
.equipment-table { <style lang="scss">
padding: 20px; .echarts-box {
background: #fff; width: 100% !important;
border-radius: 8px; height: 100% !important;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1); min-width: 300px;
min-height: 300px;
} }
.title { ::-webkit-scrollbar {
text-align: center; width: 6px;
font-weight: bold; height: 6px;
font-size: 18px;
margin-bottom: 20px;
color: #2CBAB2;
} }
.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%; width: 100%;
// background-color: #00a288; height: calc(99vh - 84px);
// color: white; padding: 16px;
position: relative; box-sizing: border-box;
height: 40px; background: #f5f7fa;
font-size: 12px; overflow: auto;
display: flex;
flex-direction: column;
gap: 16px;
} }
.diagonal-line { .chart-card {
position: absolute; background: #fff;
top: 0; border-radius: 4px;
left: 0; 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%; width: 100%;
height: 100%; height: 100%;
background: linear-gradient(
to top right,
transparent 49.5%,
white 49.5%,
white 50.5%,
transparent 50.5%
);
} }
.header-top { .table-card {
position: absolute; background: #fff;
top: 4px; border-radius: 4px;
right: 6px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
z-index: 1; padding: 16px;
} height: 99vh;
box-sizing: border-box;
.header-bottom { width: 100%;
position: absolute; flex: 1;
bottom: 4px; display: flex;
left: 8px; flex-direction: column;
z-index: 1; gap: 12px;
} }
.link { .link {
color: #2CBAB2 ; color: #2CBAB2;
cursor: pointer; cursor: pointer;
text-decoration: underline;
} }
.link:hover { .link:hover {
color: #23b8b1; 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> </style>

File diff suppressed because it is too large Load Diff