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

View File

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