This commit is contained in:
kongduo
2026-06-12 17:07:17 +08:00
parent 9502d4623a
commit d2ab388cd6
2 changed files with 181 additions and 68 deletions

View File

@@ -102,6 +102,7 @@
<div class="map-corner bottom-left"></div>
<div class="map-corner bottom-right"></div>
<div class="map-radar-glow"></div>
<div class="map-3d-stage"></div>
<div class="map-title-bar">
<span class="map-title-main">全国设备态势</span>
<span class="map-title-sub">REAL-TIME DISTRIBUTION</span>
@@ -233,6 +234,10 @@
let resizeObserver = null
const mapInstance = ref(null)
const markers = ref([])
let buildingsLayer = null
const default3DCenter = [120.153576, 30.287459]
const default3DZoom = 17.6
const tableData = ref([])
@@ -391,12 +396,38 @@
}))
console.log('devices', devices.value)
nextTick(() => {
if (mapInstance.value) {
mapInstance.value.setZoomAndCenter(default3DZoom, getValidDeviceCenter())
}
addMarkers()
})
}
})
}
const getValidDeviceCenter = () => {
const firstValidDevice = devices.value.find((device) => {
const longitude = Number(device.gatewayLong)
const latitude = Number(device.gatewayLat)
return Number.isFinite(longitude) && Number.isFinite(latitude)
})
if (!firstValidDevice) return default3DCenter
return [Number(firstValidDevice.gatewayLong), Number(firstValidDevice.gatewayLat)]
}
const add3DBuildingLayer = () => {
if (!mapInstance.value || typeof AMap === 'undefined' || !AMap.Buildings) return
buildingsLayer = new AMap.Buildings({
zooms: [3, 20],
heightFactor: 1.6,
wallColor: 'rgba(42, 169, 255, 0.88)',
roofColor: 'rgba(139, 233, 255, 0.78)'
})
mapInstance.value.add(buildingsLayer)
}
const initMap = () => {
if (typeof AMap === 'undefined') {
console.error('高德地图API未加载')
@@ -406,21 +437,32 @@
console.log('开始初始化地图')
mapInstance.value = new AMap.Map('chinaMap', {
zoom: 4.8,
center: [105.6, 36.4],
mapStyle: 'amap://styles/darkblue',
zoom: default3DZoom,
zooms: [3, 20],
center: getValidDeviceCenter(),
mapStyle: 'amap://styles/blue',
viewMode: '3D',
pitch: 34,
rotation: -8,
pitch: 68,
rotation: -28,
terrain: true,
pitchEnable: true,
rotateEnable: true,
dragEnable: true,
zoomEnable: true,
buildingAnimation: true,
showBuildingBlock: true,
features: ['bg', 'road', 'building', 'point'],
showLabel: false,
skyColor: '#020713'
})
add3DBuildingLayer()
mapInstance.value.on('complete', () => {
console.log('地图加载完成')
// 地图加载完成后,再尝试添加标记点
if (devices.value && devices.value.length > 0) {
const center = getValidDeviceCenter()
mapInstance.value.setZoomAndCenter(default3DZoom, center)
addMarkers()
}
})
@@ -450,8 +492,12 @@
testData.forEach((device, index) => {
// console.log(`渲染第 ${index} 个点:`, device)
const longitude = Number(device.gatewayLong)
const latitude = Number(device.gatewayLat)
if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return
const marker = new AMap.Marker({
position: [device.gatewayLong, device.gatewayLat],
position: [longitude, latitude],
content: createMarkerContent(device.status),
anchor: 'bottom-center',
zIndex: 120
@@ -584,53 +630,63 @@
.device-stat-content {
display: flex;
// gap: 0.2rem;
align-items: center;
gap: 0.1rem;
flex: 1;
min-height: 0;
overflow: hidden;
}
.device-ring-chart {
flex: 1;
min-height: 1.5rem;
height: 100%;
width: 100%;
flex: 0 0 1.58rem;
width: 1.58rem;
height: 1.58rem;
min-width: 1.58rem;
min-height: 1.58rem;
}
.device-list {
flex: 0.8;
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
// gap: 0.12rem;
justify-content: center;
gap: 0.03rem;
overflow: visible;
}
.device-item {
display: flex;
align-items: center;
gap: 0.12rem;
font-size: 0.18rem;
gap: 0.05rem;
min-width: 0;
font-size: 0.12rem;
font-weight: bold;
white-space: nowrap;
}
.device-dot {
width: 0.1rem;
height: 0.1rem;
flex: 0 0 auto;
width: 0.08rem;
height: 0.08rem;
border-radius: 2px;
}
.device-name {
color: var(--tech-text);
flex: 1;
flex: 1 1 auto;
min-width: 0;
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
overflow: visible;
}
.device-count {
color: var(--tech-text);
flex: 0 0 auto;
font-weight: bold;
font-size: 0.14rem;
margin-right: 0.06rem;
font-size: 0.12rem;
margin-right: 0.01rem;
white-space: nowrap;
}
@@ -1099,8 +1155,47 @@
margin: 0;
overflow: hidden;
background:
radial-gradient(circle at 50% 48%, rgba(0, 212, 255, 0.18), transparent 38%),
linear-gradient(180deg, rgba(2, 8, 23, 0.18), rgba(2, 8, 23, 0.55));
radial-gradient(ellipse at 50% 64%, rgba(0, 212, 255, 0.3), transparent 45%),
linear-gradient(180deg, rgba(8, 47, 73, 0.04), rgba(2, 8, 23, 0.42));
perspective: 8rem;
}
.map-container-full .amap-layer,
.map-container-full .amap-maps,
.map-container-full canvas {
filter: brightness(1.22) saturate(1.18) contrast(1.06);
}
.map-container-full::before {
content: '';
position: absolute;
left: 8%;
right: 8%;
bottom: 0.34rem;
z-index: 998;
height: 2.4rem;
pointer-events: none;
background:
repeating-linear-gradient(90deg, rgba(0, 212, 255, 0.18) 0 1px, transparent 1px 0.38rem),
repeating-linear-gradient(0deg, rgba(0, 212, 255, 0.16) 0 1px, transparent 1px 0.38rem),
radial-gradient(ellipse at 50% 50%, rgba(0, 212, 255, 0.16), transparent 68%);
border: 1px solid rgba(0, 212, 255, 0.2);
border-radius: 50%;
box-shadow: 0 0 0.42rem rgba(0, 212, 255, 0.1);
transform: rotateX(68deg) translateY(0.2rem);
transform-origin: center bottom;
mix-blend-mode: screen;
}
.map-container-full::after {
content: '';
position: absolute;
inset: 0;
z-index: 999;
pointer-events: none;
background:
linear-gradient(180deg, rgba(137, 221, 255, 0.16), transparent 16%, transparent 72%, rgba(2, 8, 23, 0.24)),
linear-gradient(90deg, rgba(2, 8, 23, 0.28), transparent 16%, transparent 84%, rgba(2, 8, 23, 0.28));
}
.map-control {
@@ -1204,22 +1299,39 @@
.map-radar-glow {
position: absolute;
left: 50%;
top: 52%;
top: 58%;
z-index: 1000;
width: 5.2rem;
height: 5.2rem;
width: 5.6rem;
height: 5.6rem;
border: 1px solid rgba(0, 212, 255, 0.22);
border-radius: 50%;
background:
repeating-radial-gradient(circle, transparent 0 0.62rem, rgba(0, 212, 255, 0.08) 0.63rem 0.64rem),
conic-gradient(from 0deg, transparent 0deg, rgba(0, 212, 255, 0.2) 46deg, transparent 88deg, transparent 360deg);
box-shadow: inset 0 0 0.42rem rgba(0, 212, 255, 0.08), 0 0 0.36rem rgba(0, 212, 255, 0.08);
transform: translate(-50%, -50%);
animation: radarRotate 12s linear infinite;
transform: translate(-50%, -50%) rotateX(62deg);
transform-origin: center center;
animation: radarRotate3d 12s linear infinite;
mix-blend-mode: screen;
pointer-events: none;
}
.map-3d-stage {
position: absolute;
left: 50%;
bottom: 0.28rem;
z-index: 1000;
width: 5.8rem;
height: 1.55rem;
pointer-events: none;
border: 1px solid rgba(245, 158, 11, 0.24);
border-radius: 50%;
background: radial-gradient(ellipse at center, rgba(245, 158, 11, 0.14), transparent 62%);
box-shadow: 0 0 0.32rem rgba(245, 158, 11, 0.1), inset 0 0 0.28rem rgba(0, 212, 255, 0.08);
transform: translateX(-50%);
mix-blend-mode: screen;
}
.map-corner {
position: absolute;
z-index: 1000;
@@ -1392,50 +1504,36 @@
.container .right .module-card {
position: relative;
overflow: hidden;
border: 1px solid rgba(0, 212, 255, 0.16);
border-radius: 0.08rem;
transform: scale(0.94);
transform-origin: center center;
border: none;
border-radius: 0;
background:
linear-gradient(135deg, rgba(14, 165, 233, 0.12), rgba(15, 23, 42, 0.28) 44%, rgba(245, 158, 11, 0.07)),
linear-gradient(180deg, rgba(15, 23, 42, 0.5), rgba(2, 6, 23, 0.18));
box-shadow:
inset 0 0 0.22rem rgba(0, 212, 255, 0.08),
0 0.08rem 0.28rem rgba(0, 0, 0, 0.28);
backdrop-filter: blur(4px);
transition: transform 0.35s ease, border-color 0.35s ease, box-shadow 0.35s ease;
&::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(135deg, rgba(0, 212, 255, 0.78), transparent 32%, transparent 62%, rgba(245, 158, 11, 0.55));
mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
mask-composite: exclude;
pointer-events: none;
opacity: 0.75;
}
radial-gradient(circle at 12% 12%, rgba(0, 212, 255, 0.08), transparent 42%),
linear-gradient(135deg, rgba(14, 165, 233, 0.05), rgba(15, 23, 42, 0.1) 48%, rgba(245, 158, 11, 0.035));
box-shadow: none;
backdrop-filter: none;
transition: background 0.35s ease, transform 0.35s ease;
&::after {
content: '';
position: absolute;
top: 0;
left: -65%;
width: 45%;
left: -70%;
width: 38%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(0, 212, 255, 0.16), transparent);
background: linear-gradient(90deg, transparent, rgba(0, 212, 255, 0.08), transparent);
transform: skewX(-18deg);
animation: cardGlint 7s ease-in-out infinite;
pointer-events: none;
mix-blend-mode: screen;
}
&:hover {
transform: translateY(-0.02rem);
border-color: rgba(0, 212, 255, 0.42);
box-shadow:
inset 0 0 0.28rem rgba(0, 212, 255, 0.13),
0 0.12rem 0.32rem rgba(0, 0, 0, 0.34),
0 0 0.22rem rgba(0, 212, 255, 0.18);
transform: scale(0.98);
background:
radial-gradient(circle at 12% 12%, rgba(0, 212, 255, 0.11), transparent 44%),
linear-gradient(135deg, rgba(14, 165, 233, 0.07), rgba(15, 23, 42, 0.12) 48%, rgba(245, 158, 11, 0.04));
}
}
@@ -1447,24 +1545,26 @@
content: '';
position: absolute;
left: 0;
right: 0;
right: 18%;
bottom: -0.06rem;
height: 1px;
background: linear-gradient(90deg, rgba(0, 212, 255, 0.8), rgba(59, 130, 246, 0.2), transparent);
box-shadow: 0 0 0.12rem rgba(0, 212, 255, 0.55);
background: linear-gradient(90deg, rgba(0, 212, 255, 0.34), rgba(59, 130, 246, 0.08), transparent);
box-shadow: 0 0 0.08rem rgba(0, 212, 255, 0.18);
opacity: 0.55;
}
}
.container .module-title {
color: #eefaff;
font-size: 0.18rem;
letter-spacing: 0.02rem;
text-shadow: 0 0 0.12rem rgba(0, 212, 255, 0.55);
}
.container .title-icon {
position: relative;
width: 0.08rem;
height: 0.26rem;
width: 0.07rem;
height: 0.23rem;
background: linear-gradient(180deg, #f59e0b 0%, #00e5ff 45%, #2563eb 100%);
box-shadow: 0 0 0.12rem rgba(0, 212, 255, 0.9);
animation: iconPulse 1.8s ease-in-out infinite;
@@ -1673,6 +1773,10 @@
}
}
.container .device-item {
padding: 0.04rem 0.04rem;
}
.container .device-dot,
.container .alert-dot,
.container .status-dot {
@@ -1988,6 +2092,15 @@
}
}
@keyframes radarRotate3d {
0% {
transform: translate(-50%, -50%) rotateX(62deg) rotate(0deg);
}
100% {
transform: translate(-50%, -50%) rotateX(62deg) rotate(360deg);
}
}
@keyframes controlGlint {
0%, 58% {
left: -70%;

View File

@@ -147,7 +147,7 @@ export const getDeviceStatOption = (deviceTypes) => {
series: [
{
type: 'pie',
radius: ['45%', '70%'],
radius: ['42%', '68%'],
center: ['50%', '50%'],
avoidLabelOverlap: false,
itemStyle: pieItemStyle,
@@ -155,7 +155,7 @@ export const getDeviceStatOption = (deviceTypes) => {
show: true,
position: 'center',
formatter: `{total|${total}}\n{name|设备总数}`,
rich: createCenterLabelRich(18, 12)
rich: createCenterLabelRich(16, 10)
},
labelLine: {
show: false