This commit is contained in:
bb_pan 2026-01-07 13:04:52 +08:00
parent 29905e454e
commit 05e2f8c3cc
7 changed files with 9 additions and 1652 deletions

View File

@ -434,6 +434,7 @@ export default {
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)
@ -445,6 +446,8 @@ export default {
})
} else if (this.btnIndex == 2) {
res = await getEquipmentNumberApi()
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)

View File

@ -1,413 +0,0 @@
<template>
<div class="middle-warp">
<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>
<!-- 外层容器用于加阴影 -->
<div class="map-wrapper">
<!-- 倾斜层实现 3D 倾斜效果 -->
<div class="map-tilt">
<div ref="mapChart" class="map-container"></div>
</div>
</div>
<!-- 点击城市显示详情 -->
<div
v-if="selectedCity && btnIndex == 1"
class="city-tooltip"
:style="{ left: tooltipPos.x + 25 + 'px', top: tooltipPos.y + 25 + 'px' }"
>
<div class="city-name">{{ selectedCity.cityName }}</div>
<div
>装备价值 <span class="num">{{ selectedCity.totalValue }}</span
><span class="unit"> 万元</span></div
>
<div
>装备数量 <span class="num">{{ selectedCity.totalEquipmentQuantity }}</span
><span class="unit"> </span></div
>
<div
>配置率 <span class="num">{{ selectedCity.configRate || 0 }}</span
><span class="unit"> %</span></div
>
<div
>线路数量 <span class="num">{{ selectedCity.lineNum }}</span
><span class="unit"> </span></div
>
<div
>变电数量 <span class="num">{{ selectedCity.substationNum }}</span
><span class="unit"> </span></div
>
<div
>电缆数量 <span class="num">{{ selectedCity.cableNum }}</span
><span class="unit"> </span></div
>
</div>
</div>
</template>
<script>
import 'echarts-gl'
import * as echarts from 'echarts'
import anhuiMapJson from '../anhui.json'
import labelBg from '../../img/value-bg.png'
import { getUnitEquipmentConfigurationApi, getEquipmentNumberApi, getMechanizationRateApi } from '@/api/wsScreen'
export default {
data() {
return {
btnIndex: 1,
myChart: null,
selectedCity: null,
tooltipPos: { x: 0, y: 0 },
cityData: [
//
// {
// cityName: '',
// deptName: '',
// value: [117.227239, 31.820586, 100],
// totalValue: 100,
// totalEquipmentQuantity: 100,
// configRate: 100,
// lineNum: 100,
// substationNum: 100,
// cableNum: 100,
// },
// {
// cityName: '',
// deptName: '',
// value: [118.62807, 31.68005, 100],
// totalValue: 100,
// totalEquipmentQuantity: 100,
// configRate: 100,
// lineNum: 100,
// substationNum: 100,
// cableNum: 100,
// },
],
}
},
created() {
this.getInfo()
// setTimeout(() => {
// console.log('🚀 ~ ~ this.cityData:', this.cityData)
// this.initMap()
// }, 300)
},
methods: {
handleBtn(index) {
this.btnIndex = index
this.cityData = []
this.getInfo()
},
async getInfo() {
try {
let res = null
if (this.btnIndex == 1) {
res = await getUnitEquipmentConfigurationApi()
if (!res.data) return
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.totalValue)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
}
})
} else if (this.btnIndex == 2) {
res = await getEquipmentNumberApi()
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.num)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
deptName: item.name,
}
})
} else if (this.btnIndex == 3) {
// res = await getMechanizationRateApi()
}
setTimeout(() => {
console.log('🚀 ~ 地图数据 ~ this.cityData:', this.cityData)
this.initMap()
}, 300)
console.log('🚀 ~ 地图数据 ~ res:', res)
} catch (error) {
console.log('🚀 ~ 地图数据 ~ error:', error)
// this.initMap()
}
},
initMap() {
echarts.registerMap('anhui', anhuiMapJson)
this.myChart = echarts.init(this.$refs.mapChart)
const seriesData = this.cityData
const option = {
backgroundColor: 'transparent',
geo3D: {
map: 'anhui',
roam: false, // 👈
regionHeight: 7, // 👈
shading: 'lambert', // realistic / lambert / color
realisticMaterial: {
roughness: 0.6,
metalness: 0.1,
texture: '#3A5EB1',
},
itemStyle: {
color: '#3A5EB1', // 👈
borderColor: '#ECFEFF', // 👈
borderWidth: 2,
opacity: 1,
},
label: {
show: false,
},
light: {
main: {
intensity: 1.2,
shadow: true,
shadowQuality: 'high',
alpha: 40, //
beta: 20,
},
ambient: {
intensity: 0.6,
},
},
viewControl: {
distance: 168, //
alpha: 52, //
beta: -16,
rotateSensitivity: 0, //
zoomSensitivity: 0, //
panSensitivity: 0, //
center: [12, -30, 0],
// scale: [1.5, 1, 1]
},
},
series: [
{
type: 'scatter3D',
coordinateSystem: 'geo3D',
data: seriesData,
symbol: 'circle',
symbolSize: 18,
itemStyle: {
color: '#f90', // 👈
opacity: 1, //
},
label: {
show: true,
position: 'top', // 'top'
offset: [0, -10], // 10pxx 0y -10
triggerEvent: true,
formatter: (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.deptName
}}`
: `{name|${params.data.deptName}}`
},
rich: {
val: {
backgroundColor: { image: labelBg },
height: 28,
minWidth: 70,
lineHeight: 28,
padding: [0, 10],
align: 'center',
verticalAlign: 'middle',
color: '#fff',
fontSize: 18,
},
name: {
color: '#fff',
fontFamily: 'DS-TITLE',
fontSize: 16,
padding: [6, 0, 0, 0],
},
},
},
},
],
}
this.myChart.setOption(option)
//
this.myChart.on('click', (params) => {
console.log('🚀 ~ click ~ params:', params)
// params.name
let city =
params.data.deptName == '安徽送变电工程有限公司' ? '安徽送变电' : params.data.cityName.replace(/市$/, '')
// if (params.seriesType === 'scatter' && params.data) {
// city =
// params.data.deptName == '' ? '' : params.data.cityName.replace(/$/, '')
// } else {
// city = params.name.replace(/$/, '')
// }
this.$router.push({
path: '/screen/cityScreen',
query: {
cityName: city,
},
})
})
//
this.myChart.on('mouseover', (params) => {
console.log('🚀 ~ mouseover ~ params:', params)
let city = params.data
// if (params.seriesType === 'scatter' && params.data) {
// // console.log('🚀 ~ ~ params.data:', params.data)
// console.log('🚀 ~ mouseover ~ city:', city)
// } else {
// this.selectedCity = null
// city = this.cityData.find((c) => c.cityName === params.name)
// }
this.selectedCity = city || null
if (params.event) {
this.tooltipPos = {
x: params.event.offsetX,
y: params.event.offsetY,
}
}
})
//
this.myChart.on('mouseout', () => {
this.selectedCity = null
})
window.addEventListener('resize', () => {
this.myChart.resize()
})
},
},
beforeDestroy() {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
},
}
</script>
<style lang="scss" scoped>
.middle-warp {
margin-top: 80px;
position: relative;
padding-top: 20px;
border-radius: 8px;
overflow: hidden;
}
.top-btns {
margin-left: -80px;
display: flex;
justify-content: center;
.btn:not(:last-child) {
margin-right: 80px;
}
.btn {
font-size: 18px;
text-align: center;
padding: 6px 15px;
font-family: DS-TITLE;
color: #ccc;
background-image: url('../../img/btn.png');
background-size: 100% 100%;
cursor: pointer;
}
.active {
background-image: url('../../img/btn-active.png');
background-size: 100% 100%;
}
}
//
.map-wrapper {
/* margin-top: -180px; */
position: relative;
filter: drop-shadow(0 20px 20px rgba(0, 0, 0, 0.3)); // 20px
transform: translateZ(0); //
}
// 15 3D
.map-tilt {
transform: rotateX(15deg);
transform-origin: bottom center;
perspective: 1200px; // 👈
transition: transform 0.3s ease;
pointer-events: none;
height: 900px;
display: flex;
justify-content: center;
}
//
.middle-warp:hover .map-tilt {
transform: rotateX(12deg);
}
//
.map-container {
width: 160%;
height: 900px;
background: transparent;
pointer-events: auto; //
display: flex;
align-items: center;
justify-content: center;
}
.city-tooltip {
position: absolute;
top: 20px;
right: 80px;
width: 288px;
height: 288px;
color: #fdfdfd;
padding: 12px;
border-radius: 6px;
font-size: 17px;
background-image: url('../../img/dialog.png');
background-size: 100% 100%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 10;
.city-name {
font-size: 25px;
font-family: DS-TITLE;
margin: -5px 0 10px;
}
div {
padding-bottom: 5px;
}
//
div:not(:first-child):not(:last-child) {
margin-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.num {
font-weight: 900;
}
.unit {
font-size: 12px;
color: #ccc;
}
}
</style>

View File

@ -1,413 +0,0 @@
<template>
<div class="middle-warp">
<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>
<!-- 外层容器用于加阴影 -->
<div class="map-wrapper">
<!-- 倾斜层实现 3D 倾斜效果 -->
<div class="map-tilt">
<div ref="mapChart" class="map-container"></div>
</div>
</div>
<!-- 点击城市显示详情 -->
<div v-if="selectedCity && btnIndex == 1" class="city-tooltip">
<div class="city-name">{{ selectedCity.cityName }}</div>
<div
>装备价值 <span class="num">{{ selectedCity.totalValue }}</span
><span class="unit"> </span></div
>
<div
>装备数量 <span class="num">{{ selectedCity.totalEquipmentQuantity }}</span
><span class="unit"> </span></div
>
<div
>配置率 <span class="num">{{ selectedCity.configRate || 0 }}</span
><span class="unit"> %</span></div
>
<div
>线路数量 <span class="num">{{ selectedCity.lineNum }}</span
><span class="unit"> </span></div
>
<div
>变电数量 <span class="num">{{ selectedCity.substationNum }}</span
><span class="unit"> </span></div
>
<div
>电缆数量 <span class="num">{{ selectedCity.cableNum }}</span
><span class="unit"> </span></div
>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import anhuiMapJson from '../anhui.json'
import labelBg from '../../img/value-bg.png'
import { getUnitEquipmentConfigurationApi, getEquipmentNumberApi, getMechanizationRateApi } from '@/api/wsScreen'
export default {
data() {
return {
btnIndex: 1,
myChart: null,
selectedCity: null,
cityData: [
//
{
deptName: '合肥市',
cityName: '合肥市',
value: [117.227239, 31.820586, 100],
totalValue: 100,
totalEquipmentQuantity: 100,
configRate: 100,
lineNum: 100,
substationNum: 100,
cableNum: 100,
},
{
deptName: '芜湖市',
cityName: '芜湖市',
value: [118.62807, 31.68005, 100],
totalValue: 100,
totalEquipmentQuantity: 100,
configRate: 100,
lineNum: 100,
substationNum: 100,
cableNum: 100,
},
],
}
},
created() {
// this.getInfo()
setTimeout(() => {
console.log('🚀 ~ 地图数据 ~ this.cityData:', this.cityData)
this.initMap()
}, 300)
},
methods: {
handleBtn(index) {
this.btnIndex = index
this.cityData = []
// this.getInfo()
},
async getInfo() {
try {
let res = null
if (this.btnIndex == 1) {
res = await getUnitEquipmentConfigurationApi()
if (!res.data) return
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.totalValue)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
}
})
} else if (this.btnIndex == 2) {
res = await getEquipmentNumberApi()
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.num)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
deptName: item.name,
}
})
} else if (this.btnIndex == 3) {
// res = await getMechanizationRateApi()
}
setTimeout(() => {
console.log('🚀 ~ 地图数据 ~ this.cityData:', this.cityData)
this.initMap()
}, 300)
console.log('🚀 ~ 地图数据 ~ res:', res)
} catch (error) {
console.log('🚀 ~ 地图数据 ~ error:', error)
// this.initMap()
}
},
initMap() {
echarts.registerMap('anhui', anhuiMapJson)
this.myChart = echarts.init(this.$refs.mapChart)
const seriesData = this.cityData
const option = {
backgroundColor: 'transparent',
geo: {
map: 'anhui',
roam: false,
zoom: 1.1,
aspectScale: 1.5,
center: [117.227239, 31.820586],
label: { show: false },
itemStyle: {
areaColor: '#1B3452',
borderColor: '#5A9BD9',
borderWidth: 1.5,
},
emphasis: {
itemStyle: { areaColor: '#4C7DBF' },
},
},
series: [
// ******** 3D ********
...[1, 2, 3].map((i) => ({
type: 'map',
map: 'anhui',
roam: false,
silent: true,
zoom: 1.1,
aspectScale: 1.5,
center: [117.227239 + i * 0.05, 31.820586 - i * 0.05], // 👈
itemStyle: {
areaColor: `rgba(27,52,82,${0.6 - i * 0.15})`, //
borderColor: 'rgba(0,0,0,0.2)',
borderWidth: 1,
},
z: 1, //
})),
// ******** ********
{
type: 'map',
map: 'anhui',
roam: false,
zoom: 1.1,
aspectScale: 1.5,
center: [117.227239, 31.820586],
itemStyle: {
areaColor: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#3a8bd6' },
{ offset: 1, color: '#142b44' },
]),
borderColor: '#5A9BD9',
borderWidth: 2,
shadowColor: 'rgba(0,0,0,0.6)',
shadowBlur: 15,
},
emphasis: {
itemStyle: {
areaColor: '#4C7DBF',
},
},
z: 5,
},
// ******** ********
{
type: 'scatter',
coordinateSystem: 'geo',
data: seriesData,
symbol: 'circle',
symbolSize: 16,
label: {
show: true,
color: '#fff',
fontSize: 15,
textShadowColor: '#000',
textShadowBlur: 2,
formatter: (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.deptName}}`
: `{name|${params.data.deptName}}`
},
rich: {
val: {
backgroundColor: { image: labelBg },
height: 28,
minWidth: 70,
lineHeight: 28,
padding: [0, 10],
align: 'center',
verticalAlign: 'middle',
color: '#fff',
fontSize: 18,
},
name: {
color: '#fff',
fontFamily: 'DS-TITLE',
fontSize: 16,
padding: [6, 0, 0, 0],
},
},
},
z: 10,
},
],
}
this.myChart.setOption(option)
this.myChart.on('click', (params) => {
console.log('🚀 ~ initMap ~ params:', params)
// params.name
let city = null
if (params.seriesType === 'scatter' && params.data) {
city =
params.data.deptName == '安徽送变电工程有限公司' ? '安徽送变电' : params.data.cityName.replace(/市$/, '')
} else {
city = params.name.replace(/市$/, '')
}
this.$router.push({
path: '/screen/cityScreen',
query: {
cityName: city,
},
})
})
//
this.myChart.on('mouseover', (params) => {
// console.log('🚀 ~ initMap ~ params:', params)
let city = null
if (params.seriesType === 'scatter' && params.data) {
// console.log('🚀 ~ ~ params.data:', params.data)
city = params.data
} else {
this.selectedCity = null
city = this.cityData.find((c) => c.cityName === params.name)
}
this.selectedCity = city || null
})
//
this.myChart.on('mouseout', () => {
this.selectedCity = null
})
window.addEventListener('resize', () => {
this.myChart.resize()
})
},
},
beforeDestroy() {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
},
}
</script>
<style lang="scss" scoped>
.middle-warp {
margin-top: 80px;
position: relative;
padding-top: 20px;
border-radius: 8px;
overflow: hidden;
}
.top-btns {
margin-left: -80px;
display: flex;
justify-content: center;
.btn:not(:last-child) {
margin-right: 80px;
}
.btn {
font-size: 28px;
text-align: center;
padding: 10px 20px;
font-family: DS-TITLE;
color: #ccc;
background-image: url('../../img/btn.png');
background-size: 100% 100%;
cursor: pointer;
}
.active {
background-image: url('../../img/btn-active.png');
background-size: 100% 100%;
}
}
//
.map-wrapper {
margin-top: -21px;
position: relative;
filter: drop-shadow(0 20px 20px rgba(0, 0, 0, 0.3)); // 20px
transform: translateZ(0); //
}
// 15 3D
.map-tilt {
transform: rotateX(18deg); //
transform-origin: bottom center;
perspective: 1600px; //
transition: transform 0.3s ease;
pointer-events: none;
height: 900px;
display: flex;
justify-content: center;
}
//
.middle-warp:hover .map-tilt {
transform: rotateX(15deg); // hover
}
//
.map-container {
width: 100%;
height: 900px;
background: transparent;
pointer-events: auto; //
display: flex;
align-items: center;
justify-content: center;
}
.city-tooltip {
position: absolute;
top: 20px;
right: 80px;
width: 288px;
height: 288px;
color: #fdfdfd;
padding: 12px;
border-radius: 6px;
font-size: 17px;
background-image: url('../../img/dialog.png');
background-size: 100% 100%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 10;
.city-name {
font-size: 25px;
font-family: DS-TITLE;
margin: -5px 0 10px;
}
div {
padding-bottom: 5px;
}
//
div:not(:first-child):not(:last-child) {
margin-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.num {
font-weight: 900;
}
.unit {
font-size: 12px;
color: #ccc;
}
}
</style>

View File

@ -73,6 +73,7 @@ export default {
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)
@ -84,6 +85,8 @@ export default {
})
} else if (this.btnIndex == 2) {
res = await getEquipmentNumberApi()
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)

View File

@ -1,413 +0,0 @@
<template>
<div class="middle-warp">
<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>
<!-- 外层容器用于加阴影 -->
<div class="map-wrapper">
<!-- 倾斜层实现 3D 倾斜效果 -->
<div class="map-tilt">
<div ref="mapChart" class="map-container"></div>
</div>
</div>
<!-- 点击城市显示详情 -->
<div
v-if="selectedCity && btnIndex == 1"
class="city-tooltip"
:style="{ left: tooltipPos.x + 25 + 'px', top: tooltipPos.y + 25 + 'px' }"
>
<div class="city-name">{{ selectedCity.cityName }}</div>
<div
>装备价值 <span class="num">{{ selectedCity.totalValue }}</span
><span class="unit"> 万元</span></div
>
<div
>装备数量 <span class="num">{{ selectedCity.totalEquipmentQuantity }}</span
><span class="unit"> </span></div
>
<div
>配置率 <span class="num">{{ selectedCity.configRate || 0 }}</span
><span class="unit"> %</span></div
>
<div
>线路数量 <span class="num">{{ selectedCity.lineNum }}</span
><span class="unit"> </span></div
>
<div
>变电数量 <span class="num">{{ selectedCity.substationNum }}</span
><span class="unit"> </span></div
>
<div
>电缆数量 <span class="num">{{ selectedCity.cableNum }}</span
><span class="unit"> </span></div
>
</div>
</div>
</template>
<script>
import 'echarts-gl'
import * as echarts from 'echarts'
import anhuiMapJson from '../anhui.json'
import labelBg from '../../img/value-bg.png'
import { getUnitEquipmentConfigurationApi, getEquipmentNumberApi, getMechanizationRateApi } from '@/api/wsScreen'
export default {
data() {
return {
btnIndex: 1,
myChart: null,
selectedCity: null,
tooltipPos: { x: 0, y: 0 },
cityData: [
//
// {
// cityName: '',
// deptName: '',
// value: [117.227239, 31.820586, 100],
// totalValue: 100,
// totalEquipmentQuantity: 100,
// configRate: 100,
// lineNum: 100,
// substationNum: 100,
// cableNum: 100,
// },
// {
// cityName: '',
// deptName: '',
// value: [118.62807, 31.68005, 100],
// totalValue: 100,
// totalEquipmentQuantity: 100,
// configRate: 100,
// lineNum: 100,
// substationNum: 100,
// cableNum: 100,
// },
],
}
},
created() {
this.getInfo()
// setTimeout(() => {
// console.log('🚀 ~ ~ this.cityData:', this.cityData)
// this.initMap()
// }, 300)
},
methods: {
handleBtn(index) {
this.btnIndex = index
this.cityData = []
this.getInfo()
},
async getInfo() {
try {
let res = null
if (this.btnIndex == 1) {
res = await getUnitEquipmentConfigurationApi()
if (!res.data) return
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.totalValue)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
}
})
} else if (this.btnIndex == 2) {
res = await getEquipmentNumberApi()
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.num)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
deptName: item.name,
}
})
} else if (this.btnIndex == 3) {
// res = await getMechanizationRateApi()
}
setTimeout(() => {
console.log('🚀 ~ 地图数据 ~ this.cityData:', this.cityData)
this.initMap()
}, 300)
console.log('🚀 ~ 地图数据 ~ res:', res)
} catch (error) {
console.log('🚀 ~ 地图数据 ~ error:', error)
// this.initMap()
}
},
initMap() {
echarts.registerMap('anhui', anhuiMapJson)
this.myChart = echarts.init(this.$refs.mapChart)
const seriesData = this.cityData
const option = {
backgroundColor: 'transparent',
geo3D: {
map: 'anhui',
roam: false, // 👈
regionHeight: 7, // 👈
shading: 'lambert', // realistic / lambert / color
realisticMaterial: {
roughness: 0.6,
metalness: 0.1,
texture: '#3A5EB1',
},
itemStyle: {
color: '#3A5EB1', // 👈
borderColor: '#ECFEFF', // 👈
borderWidth: 2,
opacity: 1,
},
label: {
show: false,
},
light: {
main: {
intensity: 1.2,
shadow: true,
shadowQuality: 'high',
alpha: 40, //
beta: 20,
},
ambient: {
intensity: 0.6,
},
},
viewControl: {
distance: 168, //
alpha: 52, //
beta: -16,
rotateSensitivity: 0, //
zoomSensitivity: 0, //
panSensitivity: 0, //
center: [12, -30, 0],
// scale: [1.5, 1, 1]
},
},
series: [
{
type: 'scatter3D',
coordinateSystem: 'geo3D',
data: seriesData,
symbol: 'circle',
symbolSize: 18,
itemStyle: {
color: '#f90', // 👈
opacity: 1, //
},
label: {
show: true,
position: 'top', // 'top'
offset: [0, -10], // 10pxx 0y -10
triggerEvent: true,
formatter: (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.deptName
}}`
: `{name|${params.data.deptName}}`
},
rich: {
val: {
backgroundColor: { image: labelBg },
height: 28,
minWidth: 70,
lineHeight: 28,
padding: [0, 10],
align: 'center',
verticalAlign: 'middle',
color: '#fff',
fontSize: 18,
},
name: {
color: '#fff',
fontFamily: 'DS-TITLE',
fontSize: 16,
padding: [6, 0, 0, 0],
},
},
},
},
],
}
this.myChart.setOption(option)
//
this.myChart.on('click', (params) => {
console.log('🚀 ~ click ~ params:', params)
// params.name
let city =
params.data.deptName == '安徽送变电工程有限公司' ? '安徽送变电' : params.data.cityName.replace(/市$/, '')
// if (params.seriesType === 'scatter' && params.data) {
// city =
// params.data.deptName == '' ? '' : params.data.cityName.replace(/$/, '')
// } else {
// city = params.name.replace(/$/, '')
// }
this.$router.push({
path: '/screen/cityScreen',
query: {
cityName: city,
},
})
})
//
this.myChart.on('mouseover', (params) => {
console.log('🚀 ~ mouseover ~ params:', params)
let city = params.data
// if (params.seriesType === 'scatter' && params.data) {
// // console.log('🚀 ~ ~ params.data:', params.data)
// console.log('🚀 ~ mouseover ~ city:', city)
// } else {
// this.selectedCity = null
// city = this.cityData.find((c) => c.cityName === params.name)
// }
this.selectedCity = city || null
if (params.event) {
this.tooltipPos = {
x: params.event.offsetX,
y: params.event.offsetY,
}
}
})
//
this.myChart.on('mouseout', () => {
this.selectedCity = null
})
window.addEventListener('resize', () => {
this.myChart.resize()
})
},
},
beforeDestroy() {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
},
}
</script>
<style lang="scss" scoped>
.middle-warp {
margin-top: 80px;
position: relative;
padding-top: 20px;
border-radius: 8px;
overflow: hidden;
}
.top-btns {
margin-left: -80px;
display: flex;
justify-content: center;
.btn:not(:last-child) {
margin-right: 80px;
}
.btn {
font-size: 18px;
text-align: center;
padding: 6px 15px;
font-family: DS-TITLE;
color: #ccc;
background-image: url('../../img/btn.png');
background-size: 100% 100%;
cursor: pointer;
}
.active {
background-image: url('../../img/btn-active.png');
background-size: 100% 100%;
}
}
//
.map-wrapper {
/* margin-top: -180px; */
position: relative;
filter: drop-shadow(0 20px 20px rgba(0, 0, 0, 0.3)); // 20px
transform: translateZ(0); //
}
// 15 3D
.map-tilt {
transform: rotateX(15deg);
transform-origin: bottom center;
perspective: 1200px; // 👈
transition: transform 0.3s ease;
pointer-events: none;
height: 900px;
display: flex;
justify-content: center;
}
//
.middle-warp:hover .map-tilt {
transform: rotateX(12deg);
}
//
.map-container {
width: 160%;
height: 900px;
background: transparent;
pointer-events: auto; //
display: flex;
align-items: center;
justify-content: center;
}
.city-tooltip {
position: absolute;
top: 20px;
right: 80px;
width: 288px;
height: 288px;
color: #fdfdfd;
padding: 12px;
border-radius: 6px;
font-size: 17px;
background-image: url('../../img/dialog.png');
background-size: 100% 100%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 10;
.city-name {
font-size: 25px;
font-family: DS-TITLE;
margin: -5px 0 10px;
}
div {
padding-bottom: 5px;
}
//
div:not(:first-child):not(:last-child) {
margin-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.num {
font-weight: 900;
}
.unit {
font-size: 12px;
color: #ccc;
}
}
</style>

View File

@ -1,413 +0,0 @@
<template>
<div class="middle-warp">
<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>
<!-- 外层容器用于加阴影 -->
<div class="map-wrapper">
<!-- 倾斜层实现 3D 倾斜效果 -->
<div class="map-tilt">
<div ref="mapChart" class="map-container"></div>
</div>
</div>
<!-- 点击城市显示详情 -->
<div v-if="selectedCity && btnIndex == 1" class="city-tooltip">
<div class="city-name">{{ selectedCity.cityName }}</div>
<div
>装备价值 <span class="num">{{ selectedCity.totalValue }}</span
><span class="unit"> </span></div
>
<div
>装备数量 <span class="num">{{ selectedCity.totalEquipmentQuantity }}</span
><span class="unit"> </span></div
>
<div
>配置率 <span class="num">{{ selectedCity.configRate || 0 }}</span
><span class="unit"> %</span></div
>
<div
>线路数量 <span class="num">{{ selectedCity.lineNum }}</span
><span class="unit"> </span></div
>
<div
>变电数量 <span class="num">{{ selectedCity.substationNum }}</span
><span class="unit"> </span></div
>
<div
>电缆数量 <span class="num">{{ selectedCity.cableNum }}</span
><span class="unit"> </span></div
>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import anhuiMapJson from '../anhui.json'
import labelBg from '../../img/value-bg.png'
import { getUnitEquipmentConfigurationApi, getEquipmentNumberApi, getMechanizationRateApi } from '@/api/wsScreen'
export default {
data() {
return {
btnIndex: 1,
myChart: null,
selectedCity: null,
cityData: [
//
{
deptName: '合肥市',
cityName: '合肥市',
value: [117.227239, 31.820586, 100],
totalValue: 100,
totalEquipmentQuantity: 100,
configRate: 100,
lineNum: 100,
substationNum: 100,
cableNum: 100,
},
{
deptName: '芜湖市',
cityName: '芜湖市',
value: [118.62807, 31.68005, 100],
totalValue: 100,
totalEquipmentQuantity: 100,
configRate: 100,
lineNum: 100,
substationNum: 100,
cableNum: 100,
},
],
}
},
created() {
// this.getInfo()
setTimeout(() => {
console.log('🚀 ~ 地图数据 ~ this.cityData:', this.cityData)
this.initMap()
}, 300)
},
methods: {
handleBtn(index) {
this.btnIndex = index
this.cityData = []
// this.getInfo()
},
async getInfo() {
try {
let res = null
if (this.btnIndex == 1) {
res = await getUnitEquipmentConfigurationApi()
if (!res.data) return
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.totalValue)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
}
})
} else if (this.btnIndex == 2) {
res = await getEquipmentNumberApi()
this.cityData = res.data.map((item) => {
let value = item.location.split(',')
value.push(item.num)
// console.log('🚀 ~ getInfo ~ value:', value)
return {
...item,
value,
deptName: item.name,
}
})
} else if (this.btnIndex == 3) {
// res = await getMechanizationRateApi()
}
setTimeout(() => {
console.log('🚀 ~ 地图数据 ~ this.cityData:', this.cityData)
this.initMap()
}, 300)
console.log('🚀 ~ 地图数据 ~ res:', res)
} catch (error) {
console.log('🚀 ~ 地图数据 ~ error:', error)
// this.initMap()
}
},
initMap() {
echarts.registerMap('anhui', anhuiMapJson)
this.myChart = echarts.init(this.$refs.mapChart)
const seriesData = this.cityData
const option = {
backgroundColor: 'transparent',
geo: {
map: 'anhui',
roam: false,
zoom: 1.1,
aspectScale: 1.5,
center: [117.227239, 31.820586],
label: { show: false },
itemStyle: {
areaColor: '#1B3452',
borderColor: '#5A9BD9',
borderWidth: 1.5,
},
emphasis: {
itemStyle: { areaColor: '#4C7DBF' },
},
},
series: [
// ******** 3D ********
...[1, 2, 3].map((i) => ({
type: 'map',
map: 'anhui',
roam: false,
silent: true,
zoom: 1.1,
aspectScale: 1.5,
center: [117.227239 + i * 0.05, 31.820586 - i * 0.05], // 👈
itemStyle: {
areaColor: `rgba(27,52,82,${0.6 - i * 0.15})`, //
borderColor: 'rgba(0,0,0,0.2)',
borderWidth: 1,
},
z: 1, //
})),
// ******** ********
{
type: 'map',
map: 'anhui',
roam: false,
zoom: 1.1,
aspectScale: 1.5,
center: [117.227239, 31.820586],
itemStyle: {
areaColor: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#3a8bd6' },
{ offset: 1, color: '#142b44' },
]),
borderColor: '#5A9BD9',
borderWidth: 2,
shadowColor: 'rgba(0,0,0,0.6)',
shadowBlur: 15,
},
emphasis: {
itemStyle: {
areaColor: '#4C7DBF',
},
},
z: 5,
},
// ******** ********
{
type: 'scatter',
coordinateSystem: 'geo',
data: seriesData,
symbol: 'circle',
symbolSize: 16,
label: {
show: true,
color: '#fff',
fontSize: 15,
textShadowColor: '#000',
textShadowBlur: 2,
formatter: (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.deptName}}`
: `{name|${params.data.deptName}}`
},
rich: {
val: {
backgroundColor: { image: labelBg },
height: 28,
minWidth: 70,
lineHeight: 28,
padding: [0, 10],
align: 'center',
verticalAlign: 'middle',
color: '#fff',
fontSize: 18,
},
name: {
color: '#fff',
fontFamily: 'DS-TITLE',
fontSize: 16,
padding: [6, 0, 0, 0],
},
},
},
z: 10,
},
],
}
this.myChart.setOption(option)
this.myChart.on('click', (params) => {
console.log('🚀 ~ initMap ~ params:', params)
// params.name
let city = null
if (params.seriesType === 'scatter' && params.data) {
city =
params.data.deptName == '安徽送变电工程有限公司' ? '安徽送变电' : params.data.cityName.replace(/市$/, '')
} else {
city = params.name.replace(/市$/, '')
}
this.$router.push({
path: '/screen/cityScreen',
query: {
cityName: city,
},
})
})
//
this.myChart.on('mouseover', (params) => {
// console.log('🚀 ~ initMap ~ params:', params)
let city = null
if (params.seriesType === 'scatter' && params.data) {
// console.log('🚀 ~ ~ params.data:', params.data)
city = params.data
} else {
this.selectedCity = null
city = this.cityData.find((c) => c.cityName === params.name)
}
this.selectedCity = city || null
})
//
this.myChart.on('mouseout', () => {
this.selectedCity = null
})
window.addEventListener('resize', () => {
this.myChart.resize()
})
},
},
beforeDestroy() {
if (this.myChart) {
this.myChart.dispose()
this.myChart = null
}
},
}
</script>
<style lang="scss" scoped>
.middle-warp {
margin-top: 80px;
position: relative;
padding-top: 20px;
border-radius: 8px;
overflow: hidden;
}
.top-btns {
margin-left: -80px;
display: flex;
justify-content: center;
.btn:not(:last-child) {
margin-right: 80px;
}
.btn {
font-size: 28px;
text-align: center;
padding: 10px 20px;
font-family: DS-TITLE;
color: #ccc;
background-image: url('../../img/btn.png');
background-size: 100% 100%;
cursor: pointer;
}
.active {
background-image: url('../../img/btn-active.png');
background-size: 100% 100%;
}
}
//
.map-wrapper {
margin-top: -21px;
position: relative;
filter: drop-shadow(0 20px 20px rgba(0, 0, 0, 0.3)); // 20px
transform: translateZ(0); //
}
// 15 3D
.map-tilt {
transform: rotateX(18deg); //
transform-origin: bottom center;
perspective: 1600px; //
transition: transform 0.3s ease;
pointer-events: none;
height: 900px;
display: flex;
justify-content: center;
}
//
.middle-warp:hover .map-tilt {
transform: rotateX(15deg); // hover
}
//
.map-container {
width: 100%;
height: 900px;
background: transparent;
pointer-events: auto; //
display: flex;
align-items: center;
justify-content: center;
}
.city-tooltip {
position: absolute;
top: 20px;
right: 80px;
width: 288px;
height: 288px;
color: #fdfdfd;
padding: 12px;
border-radius: 6px;
font-size: 17px;
background-image: url('../../img/dialog.png');
background-size: 100% 100%;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 10;
.city-name {
font-size: 25px;
font-family: DS-TITLE;
margin: -5px 0 10px;
}
div {
padding-bottom: 5px;
}
//
div:not(:first-child):not(:last-child) {
margin-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
}
.num {
font-weight: 900;
}
.unit {
font-size: 12px;
color: #ccc;
}
}
</style>

View File

@ -73,6 +73,7 @@ export default {
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)
@ -84,6 +85,8 @@ export default {
})
} else if (this.btnIndex == 2) {
res = await getEquipmentNumberApi()
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)