623 lines
20 KiB
Vue
623 lines
20 KiB
Vue
<template>
|
||
<div style="position: relative">
|
||
<div class="top-btns">
|
||
<!-- <div class="btn" :class="{ active: btnIndex == 1 }" @click="handleBtn(1)">总价值数</div>
|
||
<div class="btn" :class="{ active: btnIndex == 2 }" @click="handleBtn(2)">在库装备数</div>
|
||
<div class="btn" :class="{ active: btnIndex == 3 }" @click="handleBtn(3)">机械化率</div> -->
|
||
<div
|
||
class="btn"
|
||
:class="{ active: btnIndex == item.id }"
|
||
v-for="(item, index) in btnList"
|
||
:key="item.id"
|
||
@click="handleBtn(item.id)"
|
||
>
|
||
<div class="num">
|
||
<span v-if="item.id == 4"
|
||
>{{ mechanizationTotal > 0 ? mechanizationTotal : equipData[item.key] || 0 }}%</span
|
||
>
|
||
<span v-else>{{ equipData[item.key] || 0 }}</span>
|
||
</div>
|
||
<div class="name">{{ item.name }}</div>
|
||
<img v-show="btnIndex == item.id" src="@/assets/images/top2-icon.png" class="icon" alt="" />
|
||
</div>
|
||
</div>
|
||
|
||
<el-radio-group v-model="radio" class="radio-box" v-if="btnIndex == 4" @input="handleRadio">
|
||
<el-radio :label="1">线路工程</el-radio>
|
||
<el-radio :label="2">变电工程</el-radio>
|
||
<el-radio :label="3">电缆工程</el-radio>
|
||
</el-radio-group>
|
||
|
||
<div class="map-box">
|
||
<div class="echarts" id="mapChart"></div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
getUnitEquipmentConfigurationApi,
|
||
getEquipmentNumberApi,
|
||
getMechanizationRateApi,
|
||
getTotalEquipmentApi,
|
||
} from '@/api/wsScreen'
|
||
import * as echarts from 'echarts'
|
||
import 'echarts-gl'
|
||
|
||
let mapImg = require('./Map/assets/img/anhui.png')
|
||
import labelBg from './Map/assets/img/value-bg.png'
|
||
let mapJson = require('./Map/ah.json')
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
btnIndex: 1,
|
||
cityData: [],
|
||
btnList: [
|
||
{ id: 1, name: '总价值(万元)', key: 'totalPrice' },
|
||
{ id: 2, name: '装备总量(台)', key: 'total' },
|
||
{ id: 3, name: '配置率(分)', key: 'configuration' },
|
||
{ id: 4, name: '机械化率', key: 'mechanization' },
|
||
],
|
||
equipData: {
|
||
totalPrice: 0, // 总价值
|
||
total: 0, // 装备总数
|
||
configuration: 0, // 配置率
|
||
mechanization: 0, // 机械化率
|
||
},
|
||
radio: 1,
|
||
// 机械化率总数
|
||
mechanizationTotal: 0,
|
||
}
|
||
},
|
||
created() {
|
||
this.getInfo()
|
||
},
|
||
methods: {
|
||
handleBtn(index) {
|
||
if (this.btnIndex == index) return
|
||
this.btnIndex = index
|
||
this.cityData = []
|
||
this.getInfo()
|
||
},
|
||
handleRadio(val) {
|
||
this.getInfo()
|
||
},
|
||
async getInfo() {
|
||
try {
|
||
const { data } = await getTotalEquipmentApi()
|
||
this.equipData.total = data.totalEquipmentQuantity || 0
|
||
this.equipData.totalPrice = data.totalValue || 0
|
||
this.equipData.configuration = data.configuration || 0
|
||
this.equipData.mechanization = data.mechanization || 0
|
||
let res = null
|
||
if (this.btnIndex == 1) {
|
||
res = await getUnitEquipmentConfigurationApi()
|
||
if (!res.data) return
|
||
res.data = res.data.filter((item) => item.location)
|
||
this.cityData = res.data.map((item) => {
|
||
let value = item.location.split(',')
|
||
value.push(item.totalValue)
|
||
// console.log('🚀 ~ getInfo ~ value:', value)
|
||
return {
|
||
...item,
|
||
value,
|
||
}
|
||
})
|
||
}
|
||
if (this.btnIndex == 4) {
|
||
let type = ''
|
||
switch (this.radio) {
|
||
case 1:
|
||
type = '线路'
|
||
break
|
||
case 2:
|
||
type = '变电'
|
||
break
|
||
case 3:
|
||
type = '电缆'
|
||
break
|
||
}
|
||
res = await getMechanizationRateApi({ type })
|
||
if (!res.data) return
|
||
this.mechanizationTotal = res.data.find((item) => item.dataType == 'total')?.mechanizationRate
|
||
res.data = res.data.filter((item) => item.cityName)
|
||
this.cityData = res.data.map((item) => {
|
||
return {
|
||
cityName: item.cityName,
|
||
mechanizationRate: item.mechanizationRate,
|
||
dataType: item.dataType,
|
||
companyName: item.companyName,
|
||
value: [0, 0, item.mechanizationRate],
|
||
}
|
||
})
|
||
} else if (this.btnIndex == 2 || this.btnIndex == 3) {
|
||
// res = await getEquipmentNumberApi()
|
||
res = await getUnitEquipmentConfigurationApi()
|
||
if (!res.data) return
|
||
res.data = res.data.filter((item) => item.location)
|
||
this.cityData = res.data.map((item) => {
|
||
let value = item.location.split(',')
|
||
value.push(item.num)
|
||
// console.log('🚀 ~ getInfo ~ value:', value)
|
||
return {
|
||
...item,
|
||
value,
|
||
}
|
||
})
|
||
}
|
||
this.$nextTick(() => {
|
||
console.log('🚀 ~ 地图数据 ~ this.cityData:', this.cityData)
|
||
this.initChart()
|
||
})
|
||
console.log('🚀 ~ 地图数据 ~ res:', res)
|
||
} catch (error) {
|
||
console.log('🚀 ~ 地图数据 ~ error:', error)
|
||
// this.initChart()
|
||
}
|
||
},
|
||
initChart(jsonData = mapJson) {
|
||
// 模拟数据
|
||
let arr = this.cityData.map((item) => {
|
||
return {
|
||
...item,
|
||
name: item.cityName,
|
||
}
|
||
})
|
||
|
||
echarts.registerMap('ah', jsonData)
|
||
const charEle = document.getElementById('mapChart')
|
||
const myChart = echarts.init(charEle)
|
||
// let tooltip = gzjhTooltip(arr)
|
||
const option = {
|
||
tooltip: {
|
||
show: true,
|
||
backgroundColor: 'rgba(0,0,0,0)',
|
||
borderWidth: 0,
|
||
padding: 0, // 👈 关键 1:干掉外壳内边距
|
||
extraCssText: 'box-shadow:none;border-radius:0;', // 👈 关键 2:干掉阴影 & 圆角
|
||
position: function (point, params, dom, rect, size) {
|
||
const tooltipWidth = size.contentSize[0]
|
||
const tooltipHeight = size.contentSize[1]
|
||
const viewHeight = size.viewSize[1]
|
||
|
||
let x = point[0] - tooltipWidth / 2
|
||
let y = 0
|
||
|
||
if (point[1] < viewHeight / 2) {
|
||
// 👆 地图上半部分 → tooltip 显示在鼠标下方(靠顶部)
|
||
y = point[1] + 10
|
||
} else {
|
||
// 👇 地图下半部分 → tooltip 显示在鼠标上方(靠底部)
|
||
y = point[1] - tooltipHeight - 10
|
||
}
|
||
|
||
return [x, y]
|
||
},
|
||
formatter: (e) => {
|
||
let n = e.name
|
||
let res = ''
|
||
arr.forEach((data) => {
|
||
if (data.cityName === n) {
|
||
// console.log('🚀 ~ 提示数据 ~ data:', data)
|
||
if (data.cityName === '合肥市') {
|
||
const hefeiList = arr.filter((item) => item.cityName === '合肥市')
|
||
res =
|
||
'<div>' +
|
||
this.renderOneCompany(hefeiList[0], this.btnIndex) +
|
||
this.renderOneCompany(hefeiList[1], this.btnIndex) +
|
||
'</div>'
|
||
} else {
|
||
res = this.renderOneCompany(data, this.btnIndex)
|
||
}
|
||
}
|
||
})
|
||
return res
|
||
},
|
||
},
|
||
geo3D: {
|
||
map: 'ah',
|
||
zoom: 1,
|
||
roam: false,
|
||
label: { show: false },
|
||
itemStyle: {
|
||
opacity: 0, //.01 // important!
|
||
},
|
||
emphasis: {
|
||
disabled: true,
|
||
},
|
||
regionHeight: 0,
|
||
shading: 'lambert',
|
||
|
||
viewControl: {
|
||
zoomSensitivity: false,
|
||
rotateSensitivity: 0,
|
||
projection: 'orthographic',
|
||
autoRotate: false,
|
||
autoRotateAfterStill: 1,
|
||
orthographicSize: 100,
|
||
minAlpha: 20, // 上下旋转的最小 alpha 值。即视角能旋转到达最上面的角度。[ default: 5 ]
|
||
maxAlpha: 90, // 上下旋转的最大 alpha 值。即视角能旋转到达最下面的角度。[ default: 90 ]
|
||
minBeta: -360, // 左右旋转的最小 beta 值。即视角能旋转到达最左的角度。[ default: -80 ]
|
||
maxBeta: 360, // 左右旋转的最大 beta 值。即视角能旋转到达最右的角度。[ default: 80 ]
|
||
animation: false, // 是否开启动画。[ default: true ]
|
||
animationDurationUpdate: 1000, // 过渡动画的时长。[ default: 1000 ]
|
||
animationEasingUpdate: 'cubicInOut', // 过渡动画的缓动效果。[ default: cubicInOut ]
|
||
},
|
||
light: {
|
||
//光照阴影
|
||
main: {
|
||
color: '#02fce7', //光照颜色
|
||
intensity: 0.5, //光照强度
|
||
shadow: true, //是否显示阴影
|
||
shadowQuality: 'ultra', //阴影质量 ultra //阴影亮度
|
||
alpha: 30,
|
||
beta: 90,
|
||
},
|
||
ambient: {
|
||
color: '#fff',
|
||
intensity: 1,
|
||
},
|
||
},
|
||
zlevel: -8,
|
||
},
|
||
// 要显示的散点数据
|
||
series: [
|
||
{
|
||
left: 0,
|
||
top: 6,
|
||
bottom: 22,
|
||
type: 'map3D',
|
||
map: 'ah',
|
||
roam: false,
|
||
data: arr,
|
||
itemStyle: {
|
||
// color: "#4b6f52",
|
||
color: 'transparent',
|
||
borderWidth: 1,
|
||
borderColor: '#FFF',
|
||
opacity: 1,
|
||
},
|
||
emphasis: {
|
||
disabled: false, // 👈 一定要打开
|
||
label: {
|
||
show: false,
|
||
color: '#fff',
|
||
},
|
||
itemStyle: {
|
||
color: 'rgba(44, 186, 178, 0.45)', // 👈 青绿色半透明背景
|
||
borderColor: '#FFF', // 👈 高亮描边
|
||
borderWidth: 1,
|
||
},
|
||
},
|
||
regionHeight: 0,
|
||
shading: 'lambert',
|
||
viewControl: {
|
||
zoomSensitivity: false,
|
||
rotateSensitivity: 0,
|
||
projection: 'orthographic',
|
||
autoRotate: false,
|
||
autoRotateAfterStill: 1,
|
||
orthographicSize: 92,
|
||
animation: false, // 是否开启动画。[ default: true ]
|
||
animationDurationUpdate: 1000, // 过渡动画的时长。[ default: 1000 ]
|
||
animationEasingUpdate: 'cubicInOut', // 过渡动画的缓动效果。[ default: cubicInOut ]
|
||
},
|
||
label: {
|
||
show: true,
|
||
color: '#FFF',
|
||
position: 'top',
|
||
fontSize: 12,
|
||
fontWeight: '600',
|
||
},
|
||
light: {
|
||
//光照阴影
|
||
main: {
|
||
color: 'rgb(14,70,82)', //光照颜色
|
||
intensity: 0.1, //光照强度
|
||
shadow: true, //是否显示阴影
|
||
shadowQuality: 'ultra', //阴影质量 ultra //阴影亮度
|
||
alpha: 30,
|
||
beta: 90,
|
||
},
|
||
ambient: {
|
||
color: '#fff',
|
||
intensity: 1,
|
||
},
|
||
},
|
||
zlevel: 1,
|
||
zoom: 1,
|
||
scaleLimit: {
|
||
// 限制缩放的比例
|
||
min: 1, // 最小缩放比例
|
||
max: 1, // 最大缩放比例
|
||
},
|
||
},
|
||
{
|
||
type: 'scatter3D',
|
||
data: arr,
|
||
symbol: 'circle',
|
||
symbolSize: 11,
|
||
coordinateSystem: 'geo3D',
|
||
tooltip: { show: false },
|
||
label: {
|
||
show: false,
|
||
// position: 'top', // 先指定一个基准位置(如 'top')
|
||
// offset: [0, -10], // 向上偏移 10px(x 方向 0,y 方向 -10)
|
||
// triggerEvent: true,
|
||
// formatter: (params) => {
|
||
// // console.log('🚀 ~ initChart ~ params:', params)
|
||
// const val = params.data.value || []
|
||
// let unit = ''
|
||
// if (this.btnIndex === 1) unit = '万元'
|
||
// else if (this.btnIndex === 2) unit = '台'
|
||
// else if (this.btnIndex === 3) unit = '%'
|
||
// return val.length
|
||
// ? `{val|${val[2]} ${unit}}\n{name|${params.data.deptAbbreviation}}`
|
||
// : `{name|${params.data.deptAbbreviation}}`
|
||
// },
|
||
// rich: {
|
||
// val: {
|
||
// backgroundColor: { image: labelBg },
|
||
// height: 28,
|
||
// minWidth: 70,
|
||
// lineHeight: 28,
|
||
// padding: [0, 10],
|
||
// align: 'center',
|
||
// verticalAlign: 'middle',
|
||
// color: '#fff',
|
||
// fontSize: 23,
|
||
// },
|
||
// name: {
|
||
// color: '#fff',
|
||
// fontFamily: 'DS-TITLE',
|
||
// fontSize: 21,
|
||
// padding: [6, 0, 0, 0],
|
||
// },
|
||
// },
|
||
},
|
||
// 红点样式
|
||
// itemStyle: {
|
||
// color: 'rgba(217, 94, 0, 1)',
|
||
// },
|
||
// zlevel: 1,
|
||
},
|
||
],
|
||
graphic: {
|
||
elements: [
|
||
{
|
||
type: 'image',
|
||
style: {
|
||
image: mapImg, // 图片的 URL
|
||
width: 456, // 图片宽度
|
||
height: 440, // 图片高度
|
||
// 位置根据实际需求调整
|
||
x: 0,
|
||
y: 0,
|
||
},
|
||
left: 'center',
|
||
top: 'center',
|
||
},
|
||
],
|
||
},
|
||
}
|
||
myChart.setOption(option)
|
||
// 监听地图绑定双击事件(双击进入地图下一级)
|
||
myChart.on('click', (params) => {
|
||
console.log('🚀 ~ 点击 ~ params-->:', params)
|
||
console.log('🚀 ~ initChart ~ params.data-->>:', params.data)
|
||
if (!params.data) return
|
||
this.$router.push({
|
||
path: '/equipment/manage/equipment-ledger',
|
||
query: {
|
||
parentId: params.data.parentId,
|
||
deptId: params.data.deptId,
|
||
},
|
||
})
|
||
if (params.seriesType === 'map3D' && params.data) {
|
||
let city =
|
||
params.data.deptName == '安徽送变电工程有限公司' ? '安徽送变电' : params.data.name.replace(/市$/, '')
|
||
|
||
console.log('🚀 ~ initChart ~ this.$router:', this.$router)
|
||
// this.$router.push({
|
||
// path: '/screen/cityScreen',
|
||
// query: {
|
||
// cityName: city,
|
||
// },
|
||
// })
|
||
// window.open(`${window.location.origin}/screen/cityScreen?cityName=${city}`, '_blank')
|
||
}
|
||
})
|
||
myChart.on('mouseover', (params) => {
|
||
// console.log('🚀 ~ 移入 ~ params:', params)
|
||
})
|
||
myChart.on('mouseout', () => {
|
||
// this.selectedCity = null
|
||
})
|
||
return myChart
|
||
},
|
||
renderOneCompany(data, btnIndex) {
|
||
if (btnIndex == 1) {
|
||
return (
|
||
"<div style='width:260px;background:url(" +
|
||
require('./Map/assets/img/dialog-2.png') +
|
||
") no-repeat;background-size:100% 100%;margin-right:-10px;padding-bottom:30px'>" +
|
||
"<div style='height:34px;display:flex;align-items:center;padding-left:10px;color:#fff;font-size:17px'>" +
|
||
data.deptName +
|
||
'</div>' +
|
||
this.renderItem('装备价值', data.totalValue, '万元') +
|
||
'</div>'
|
||
)
|
||
}
|
||
|
||
if (btnIndex == 2) {
|
||
return (
|
||
"<div style='width:260px;background:url(" +
|
||
require('./Map/assets/img/dialog.png') +
|
||
") no-repeat;background-size:100% 100%;margin-right:-10px;padding-bottom:30px'>" +
|
||
"<div style='height:34px;display:flex;align-items:center;padding-left:10px;color:#fff;font-size:17px'>" +
|
||
data.deptName +
|
||
'</div>' +
|
||
this.renderItem('装备总量', data.totalEquipmentQuantity, '台') +
|
||
this.renderItem('线路数量', data.lineNum, '台') +
|
||
this.renderItem('变电数量', data.substationNum, '台') +
|
||
this.renderItem('电缆数量', data.cableNum, '台') +
|
||
'</div>'
|
||
)
|
||
}
|
||
|
||
if (btnIndex == 3) {
|
||
return (
|
||
"<div style='width:260px;background:url(" +
|
||
require('./Map/assets/img/dialog.png') +
|
||
") no-repeat;background-size:100% 100%;margin-right:-10px;padding-bottom:30px'>" +
|
||
"<div style='height:34px;display:flex;align-items:center;padding-left:10px;color:#fff;font-size:17px'>" +
|
||
data.deptName +
|
||
'</div>' +
|
||
this.renderItem('总配置率', data.configRate, '分') +
|
||
this.renderItem('线路设备配置率', data.valueA, '分') +
|
||
this.renderItem('变电设备配置率', data.valueB, '分') +
|
||
this.renderItem('电缆设备配置率', data.valueC, '分') +
|
||
'</div>'
|
||
)
|
||
}
|
||
|
||
if (btnIndex == 4) {
|
||
let title = ''
|
||
if (this.radio == 1) {
|
||
title = '线路工程机械化率配置率'
|
||
} else if (this.radio == 2) {
|
||
title = '变电工程机械化率配置率'
|
||
} else if (this.radio == 3) {
|
||
title = '电缆工程机械化率配置率'
|
||
}
|
||
|
||
return (
|
||
"<div style='width:260px;background:url(" +
|
||
require('./Map/assets/img/dialog-2.png') +
|
||
") no-repeat;background-size:100% 100%;margin-right:-10px;padding-bottom:30px'>" +
|
||
"<div style='height:34px;display:flex;align-items:center;padding-left:10px;color:#fff;font-size:17px'>" +
|
||
data.companyName +
|
||
'</div>' +
|
||
this.renderItem(title, data.mechanizationRate, '%') +
|
||
'</div>'
|
||
)
|
||
}
|
||
},
|
||
renderItem(label, value, unit) {
|
||
return (
|
||
"<div style='width:260px;margin-top:10px;height:25px;display:flex;align-items:center;font-size:12px;color:#666;margin-left:20px'>" +
|
||
'<div>' +
|
||
label +
|
||
':</div>' +
|
||
"<div style='margin-left:10px;font-size:16px'>" +
|
||
(value || 0) +
|
||
"<span style='font-size:12px'> " +
|
||
unit +
|
||
'</span>' +
|
||
'</div></div>'
|
||
)
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.num {
|
||
font-family: OPPOSans;
|
||
font-size: 24px;
|
||
line-height: 22px;
|
||
}
|
||
.name {
|
||
margin-top: 6px;
|
||
font-weight: 400;
|
||
font-size: 14px;
|
||
}
|
||
.icon {
|
||
width: 56px;
|
||
height: 54px;
|
||
position: absolute;
|
||
right: 0;
|
||
bottom: 0;
|
||
}
|
||
.top-btns {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
position: absolute;
|
||
z-index: 9;
|
||
|
||
.btn {
|
||
cursor: pointer;
|
||
width: 168px;
|
||
height: 80px;
|
||
background: #fff;
|
||
border-radius: 10px 10px 10px 10px;
|
||
margin-bottom: 16px;
|
||
padding: 15px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
color: #3f3f3f;
|
||
position: relative;
|
||
}
|
||
|
||
.active {
|
||
color: #fff;
|
||
background: linear-gradient(90deg, #00d2be 0%, #4eacff 100%);
|
||
}
|
||
}
|
||
|
||
.map-box {
|
||
width: 100%;
|
||
height: 440px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.echarts {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
// 多选框
|
||
.radio-box,
|
||
.circle-checkbox {
|
||
position: absolute;
|
||
right: -30px;
|
||
bottom: -10px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
z-index: 99;
|
||
}
|
||
</style>
|
||
|
||
<style>
|
||
/* 圆圈外框 */
|
||
.circle-checkbox .el-checkbox__inner {
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
/* 选中状态背景 */
|
||
.circle-checkbox .el-checkbox__input.is-checked .el-checkbox__inner {
|
||
background-color: #3bc6c1;
|
||
border-color: #3bc6c1;
|
||
}
|
||
|
||
/* 中间的“点” */
|
||
.circle-checkbox .el-checkbox__inner::after {
|
||
content: '';
|
||
width: 6px;
|
||
height: 6px;
|
||
background: #fff;
|
||
border-radius: 50%;
|
||
position: absolute;
|
||
left: 3.6px;
|
||
top: 3.6px;
|
||
transform: none;
|
||
}
|
||
</style>
|