1425 lines
36 KiB
Vue
1425 lines
36 KiB
Vue
<template>
|
||
<div
|
||
class="monitor-container"
|
||
:class="{ 'is-top2': currentStyle === 'top2', 'is-top3': currentStyle === 'top3' }"
|
||
>
|
||
<!-- 样式切换下拉框 -->
|
||
<div
|
||
class="style-switcher"
|
||
:class="{ 'is-top2': currentStyle === 'top2', 'is-top3': currentStyle === 'top3' }"
|
||
>
|
||
<label class="style-label" for="style-select">原型迭代</label>
|
||
<select id="style-select" v-model="currentStyle" class="style-select">
|
||
<option value="top3">原型-3</option>
|
||
<option value="top2">原型-2</option>
|
||
<option value="circle">原型-1</option>
|
||
</select>
|
||
</div>
|
||
|
||
<template v-if="currentStyle === 'top2' || currentStyle === 'top3'">
|
||
<div
|
||
class="top2-stage"
|
||
:class="topLayoutClass"
|
||
ref="top2Stage"
|
||
:style="{ '--top2-scale': top2Scale }"
|
||
>
|
||
<div class="top2-scale">
|
||
<div class="top2-layout">
|
||
<div class="top2-header" data-node-id="153:2111" :style="headerStyle">
|
||
<div class="top2-title" data-node-id="153:2112">火情综合监控看板</div>
|
||
</div>
|
||
|
||
<div class="top2-content">
|
||
<div class="top2-left-col">
|
||
<div class="panel-outline panel-left-top" data-node-id="153:2087">
|
||
<div class="panel-label label-metric-11" data-node-id="153:2098">视联拉流质量统计</div>
|
||
</div>
|
||
<div v-if="currentStyle === 'top3'" class="panel-outline panel-left-middle" data-node-id="153:2089">
|
||
<div class="panel-label label-metric-21" data-node-id="153:2099">AI分析质量监控</div>
|
||
</div>
|
||
<div class="panel-outline panel-left-bottom" data-node-id="271:27">
|
||
<div
|
||
class="panel-label"
|
||
:class="currentStyle === 'top3' ? 'label-metric-31' : 'label-metric-21'"
|
||
data-node-id="271:28"
|
||
>
|
||
{{ currentStyle === 'top3' ? '网络质量分析' : 'AI分析质量监控' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="top2-center-col">
|
||
<div v-if="currentStyle === 'top3'" class="top2-cards" data-node-id="270:19">
|
||
<div class="top2-card" data-node-id="277:79">
|
||
<div class="top2-card-icon">
|
||
<img :src="top2CardIcons.device" alt="" />
|
||
</div>
|
||
<div class="top2-card-text">
|
||
<div class="top2-card-label">火情设备数</div>
|
||
<div class="top2-card-value">54781</div>
|
||
<div class="top2-card-sub">截止今日数量</div>
|
||
</div>
|
||
</div>
|
||
<div class="top2-card" data-node-id="279:46">
|
||
<div class="top2-card-icon">
|
||
<img :src="top2CardIcons.stream" alt="" />
|
||
</div>
|
||
<div class="top2-card-text">
|
||
<div class="top2-card-label">拉流质量</div>
|
||
<div class="top2-card-value">85.2%</div>
|
||
<div class="top2-card-sub">拉流成功率指标</div>
|
||
</div>
|
||
</div>
|
||
<div class="top2-card" data-node-id="279:54">
|
||
<div class="top2-card-icon">
|
||
<img :src="top2CardIcons.ai" alt="" />
|
||
</div>
|
||
<div class="top2-card-text">
|
||
<div class="top2-card-label">AI分析质量</div>
|
||
<div class="top2-card-value">99.5%</div>
|
||
<div class="top2-card-sub">AI请求成功率</div>
|
||
</div>
|
||
</div>
|
||
<div class="top2-card" data-node-id="279:62">
|
||
<div class="top2-card-icon">
|
||
<img :src="top2CardIcons.cut" alt="" />
|
||
</div>
|
||
<div class="top2-card-text">
|
||
<div class="top2-card-label">切图质量</div>
|
||
<div class="top2-card-value">99.5%</div>
|
||
<div class="top2-card-sub">抽帧切图成功率</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-if="currentStyle === 'top3'" class="top3-center-block" data-node-id="303:722">
|
||
<div class="top2-center" data-node-id="303:723">
|
||
<div class="svg-wrapper is-top3" ref="svgWrapper" v-html="svgContent"></div>
|
||
</div>
|
||
<img class="top3-map-frame" :src="mapKuang" alt="" />
|
||
</div>
|
||
<div v-else class="top2-center" data-node-id="162:5">
|
||
<div class="svg-wrapper is-top2" ref="svgWrapper" v-html="svgContent"></div>
|
||
</div>
|
||
<div class="top2-bottom-row">
|
||
<div class="panel-outline panel-center-bottom-left" data-node-id="153:2091">
|
||
<div class="panel-label label-metric-22" data-node-id="153:2100">
|
||
{{ currentStyle === 'top3' ? '切图指标' : '监控指标区22' }}
|
||
</div>
|
||
</div>
|
||
<div class="panel-outline panel-center-bottom-right" data-node-id="153:2092">
|
||
<div class="panel-label label-metric-23" data-node-id="153:2102">
|
||
{{ currentStyle === 'top3' ? '魔方指标' : '监控指标区23' }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="top2-right-col">
|
||
<template v-if="currentStyle === 'top3'">
|
||
<div class="panel-outline top3-board top3-alarm" data-node-id="328:1647">
|
||
<div class="top3-board-title">告警看板</div>
|
||
<div class="top3-board-table">
|
||
<div class="top3-board-col top3-rank-col">
|
||
<div class="top3-table-head"></div>
|
||
<div class="top3-table-cell top3-rank-cell"><span class="rank-badge rank-1">1</span></div>
|
||
<div class="top3-table-cell top3-rank-cell"><span class="rank-badge rank-2">3</span></div>
|
||
<div class="top3-table-cell top3-rank-cell"><span class="rank-badge rank-3">3</span></div>
|
||
</div>
|
||
<div class="top3-board-col top3-info-col">
|
||
<div class="top3-table-head top3-table-head-left">告警信息</div>
|
||
<div class="top3-table-cell top3-info-cell">告警1</div>
|
||
<div class="top3-table-cell top3-info-cell">告警2</div>
|
||
<div class="top3-table-cell top3-info-cell">告警3</div>
|
||
</div>
|
||
<div class="top3-board-col top3-metric-col">
|
||
<div class="top3-table-head top3-table-head-right">指标</div>
|
||
<div class="top3-table-cell top3-metric-cell">网络</div>
|
||
<div class="top3-table-cell top3-metric-cell">视联网</div>
|
||
<div class="top3-table-cell top3-metric-cell">AI分析</div>
|
||
</div>
|
||
</div>
|
||
<div class="top3-board-bottom">
|
||
<div class="top3-bottom-title">告警内容</div>
|
||
<div class="top3-bottom-line"></div>
|
||
<div class="top3-bottom-line"></div>
|
||
<div class="top3-bottom-line"></div>
|
||
</div>
|
||
</div>
|
||
<div class="panel-outline top3-board top3-fault" data-node-id="328:1692">
|
||
<div class="top3-board-title">故障看板</div>
|
||
<div class="top3-board-table">
|
||
<div class="top3-board-col top3-rank-col">
|
||
<div class="top3-table-head"></div>
|
||
<div class="top3-table-cell top3-rank-cell"><span class="rank-badge rank-1">1</span></div>
|
||
<div class="top3-table-cell top3-rank-cell"><span class="rank-badge rank-2">3</span></div>
|
||
<div class="top3-table-cell top3-rank-cell"><span class="rank-badge rank-3">3</span></div>
|
||
</div>
|
||
<div class="top3-board-col top3-info-col">
|
||
<div class="top3-table-head top3-table-head-left">故障记录</div>
|
||
<div class="top3-table-cell top3-info-cell">故障1</div>
|
||
<div class="top3-table-cell top3-info-cell">故障2</div>
|
||
<div class="top3-table-cell top3-info-cell">故障3</div>
|
||
</div>
|
||
<div class="top3-board-col top3-metric-col">
|
||
<div class="top3-table-head top3-table-head-right">指标</div>
|
||
<div class="top3-table-cell top3-metric-cell">网络</div>
|
||
<div class="top3-table-cell top3-metric-cell">视联网拉流</div>
|
||
<div class="top3-table-cell top3-metric-cell">AI分析</div>
|
||
</div>
|
||
</div>
|
||
<div class="top3-board-bottom">
|
||
<div class="top3-bottom-title">故障根因</div>
|
||
<div class="top3-bottom-line"></div>
|
||
<div class="top3-bottom-line"></div>
|
||
<div class="top3-bottom-line"></div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<template v-else>
|
||
<div class="panel-outline panel-right-top" data-node-id="153:2088">
|
||
<div class="panel-label label-alarm" data-node-id="153:2095">告警看板</div>
|
||
</div>
|
||
<div class="panel-outline panel-right-bottom" data-node-id="153:2090">
|
||
<div class="panel-label label-fault" data-node-id="153:2096">故障看板</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<template v-else>
|
||
<h1 class="title">火情监控全链路业务监控视图</h1>
|
||
|
||
<div class="svg-wrapper" ref="svgWrapper" v-html="svgContent"></div>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
|
||
import videoIcon from './assets/video.svg'
|
||
import playIcon from './assets/play.svg'
|
||
import aiIcon from './assets/ai.svg'
|
||
import picIcon from './assets/pic.svg'
|
||
import mapKuang from './assets/map-kuang.svg'
|
||
import headerBack from './assets/header-back.svg'
|
||
|
||
type StyleType = 'circle' | 'top2' | 'top3'
|
||
|
||
const svgWrapper = ref<HTMLElement | null>(null)
|
||
const svgContent = ref('')
|
||
const currentStyle = ref<StyleType>('top3')
|
||
const top2Stage = ref<HTMLElement | null>(null)
|
||
const top2Scale = ref(1)
|
||
|
||
const TOP_LAYOUTS = {
|
||
top2: { width: 1296, height: 692 },
|
||
top3: { width: 1438, height: 929 },
|
||
}
|
||
let top2ResizeObserver: ResizeObserver | null = null
|
||
|
||
const top2CardIcons = {
|
||
device: videoIcon,
|
||
stream: playIcon,
|
||
ai: aiIcon,
|
||
cut: picIcon,
|
||
}
|
||
|
||
const updateTop2Scale = () => {
|
||
const stage = top2Stage.value
|
||
if (!stage) return
|
||
if (currentStyle.value === 'circle') return
|
||
const layout = TOP_LAYOUTS[currentStyle.value]
|
||
if (!layout) return
|
||
const { width, height } = stage.getBoundingClientRect()
|
||
if (!width || !height) return
|
||
const scaleX = width / layout.width
|
||
const scaleY = height / layout.height
|
||
top2Scale.value = Math.min(scaleX, scaleY)
|
||
}
|
||
|
||
const attachTop2Observer = () => {
|
||
const stage = top2Stage.value
|
||
if (!stage) return
|
||
if (!top2ResizeObserver) {
|
||
top2ResizeObserver = new ResizeObserver(() => {
|
||
updateTop2Scale()
|
||
})
|
||
} else {
|
||
top2ResizeObserver.disconnect()
|
||
}
|
||
top2ResizeObserver.observe(stage)
|
||
}
|
||
|
||
const detachTop2Observer = () => {
|
||
if (!top2ResizeObserver) return
|
||
top2ResizeObserver.disconnect()
|
||
}
|
||
|
||
const loadSvg = async (style: StyleType) => {
|
||
try {
|
||
const fileName = style === 'circle' ? 'monitor.svg' : 'top2.svg'
|
||
const response = await fetch(`/${fileName}`)
|
||
const content = await response.text()
|
||
|
||
svgContent.value = content
|
||
|
||
// 等待 DOM 更新后添加动画
|
||
setTimeout(() => {
|
||
if (style === 'circle') {
|
||
addFlowingAnimation()
|
||
} else {
|
||
addTop2Animation()
|
||
}
|
||
}, 100)
|
||
} catch (error) {
|
||
console.error('加载 SVG 失败:', error)
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
nextTick(() => {
|
||
loadSvg(currentStyle.value)
|
||
if (currentStyle.value === 'top2' || currentStyle.value === 'top3') {
|
||
attachTop2Observer()
|
||
updateTop2Scale()
|
||
}
|
||
})
|
||
})
|
||
|
||
onBeforeUnmount(() => {
|
||
detachTop2Observer()
|
||
})
|
||
|
||
watch(currentStyle, async (style) => {
|
||
await nextTick()
|
||
loadSvg(style)
|
||
if (style === 'top2' || style === 'top3') {
|
||
attachTop2Observer()
|
||
updateTop2Scale()
|
||
} else {
|
||
detachTop2Observer()
|
||
}
|
||
})
|
||
|
||
const topLayoutClass = computed(() => (
|
||
currentStyle.value === 'top3' ? 'layout-top3' : 'layout-top2'
|
||
))
|
||
|
||
const headerStyle = computed(() => (
|
||
currentStyle.value === 'top3' ? { '--header-back': `url(${headerBack})` } : {}
|
||
))
|
||
|
||
const addFlowingAnimation = () => {
|
||
if (!svgWrapper.value) return
|
||
|
||
const svg = svgWrapper.value.querySelector('svg')
|
||
if (!svg) {
|
||
console.log('SVG not found')
|
||
return
|
||
}
|
||
|
||
svg.querySelectorAll('.flowing-line').forEach((node) => node.remove())
|
||
|
||
// 查找所有可能的连线元素
|
||
const allPaths = svg.querySelectorAll('path, line, polyline')
|
||
console.log('Found elements:', allPaths.length)
|
||
|
||
let animatedCount = 0
|
||
|
||
allPaths.forEach((element, index) => {
|
||
const el = element as SVGPathElement | SVGLineElement | SVGPolylineElement
|
||
|
||
// 检查是否有描边
|
||
const hasStroke = el.getAttribute('stroke') && el.getAttribute('stroke') !== 'none'
|
||
const hasFill = el.getAttribute('fill') && el.getAttribute('fill') !== 'none'
|
||
|
||
// 只处理有描边且填充为none或没有填充的元素(这些是连线)
|
||
if (!hasStroke || (hasFill && el.getAttribute('fill') !== 'transparent')) {
|
||
return
|
||
}
|
||
|
||
// 跳过顶部第一条虚线(shape582)
|
||
const elementId = el.getAttribute('id')
|
||
if (elementId === 'shape582') {
|
||
return
|
||
}
|
||
|
||
const strokeWidth = parseFloat(el.getAttribute('stroke-width') || '1')
|
||
if (strokeWidth < 0.5) return
|
||
|
||
animatedCount++
|
||
|
||
// 保存原始描边颜色和宽度
|
||
const originalStroke = el.getAttribute('stroke') || '#595959'
|
||
const originalWidth = strokeWidth
|
||
|
||
// 判断是否为红色线条
|
||
const isRed = originalStroke.toLowerCase().includes('red') ||
|
||
originalStroke.toLowerCase().includes('#eb') ||
|
||
originalStroke.toLowerCase().includes('#ff') ||
|
||
originalStroke.toLowerCase().includes('#f') ||
|
||
originalStroke.toLowerCase().includes('rgb(235') ||
|
||
originalStroke.toLowerCase().includes('rgb(255')
|
||
|
||
// 根据线条颜色选择流动颜色
|
||
const flowColor = isRed ? '#EB5017' : '#00ff88'
|
||
|
||
// 创建流动层(克隆原线条)
|
||
const flowLine = el.cloneNode(true) as SVGElement
|
||
flowLine.setAttribute('stroke', flowColor)
|
||
flowLine.setAttribute('stroke-width', String(originalWidth * 1.2))
|
||
flowLine.setAttribute('stroke-linecap', 'round')
|
||
flowLine.setAttribute('stroke-opacity', '0.7')
|
||
flowLine.setAttribute('fill', 'none')
|
||
flowLine.classList.add('flowing-line')
|
||
|
||
// 计算路径长度
|
||
let pathLength = 0
|
||
if (flowLine instanceof SVGPathElement) {
|
||
pathLength = flowLine.getTotalLength()
|
||
} else if (flowLine instanceof SVGLineElement) {
|
||
const x1 = parseFloat(flowLine.getAttribute('x1') || '0')
|
||
const y1 = parseFloat(flowLine.getAttribute('y1') || '0')
|
||
const x2 = parseFloat(flowLine.getAttribute('x2') || '0')
|
||
const y2 = parseFloat(flowLine.getAttribute('y2') || '0')
|
||
pathLength = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
|
||
}
|
||
|
||
// 设置虚线动画 - 更明显的流动效果
|
||
const dashLength = 40
|
||
const gapLength = 80
|
||
flowLine.style.strokeDasharray = `${dashLength} ${gapLength}`
|
||
flowLine.style.strokeDashoffset = String(pathLength)
|
||
|
||
// 插入流动层到原线条之后
|
||
el.parentNode?.insertBefore(flowLine, el.nextSibling)
|
||
|
||
// 添加 CSS 动画 - 减慢速度到 8-12 秒
|
||
const duration = 8 + (index % 5)
|
||
flowLine.style.animation = `flowLine ${duration}s linear infinite`
|
||
flowLine.style.animationDelay = `${(index * 0.5) % 3}s`
|
||
})
|
||
|
||
console.log('Animated lines:', animatedCount)
|
||
|
||
// 添加 CSS 动画样式
|
||
if (!document.getElementById('flowing-animation-style')) {
|
||
const style = document.createElement('style')
|
||
style.id = 'flowing-animation-style'
|
||
style.textContent = `
|
||
@keyframes flowLine {
|
||
from {
|
||
stroke-dashoffset: 1000;
|
||
}
|
||
to {
|
||
stroke-dashoffset: -1000;
|
||
}
|
||
}
|
||
.flowing-line {
|
||
filter: drop-shadow(0 0 6px currentColor);
|
||
pointer-events: none;
|
||
}
|
||
`
|
||
document.head.appendChild(style)
|
||
}
|
||
}
|
||
|
||
// Top2 样式的动画效果
|
||
const addTop2Animation = () => {
|
||
if (!svgWrapper.value) return
|
||
|
||
const svg = svgWrapper.value.querySelector('svg')
|
||
if (!svg) {
|
||
console.log('SVG not found')
|
||
return
|
||
}
|
||
|
||
svg.querySelectorAll('.flowing-line-top2').forEach((node) => node.remove())
|
||
|
||
// 查找所有可能的连线元素
|
||
const allPaths = svg.querySelectorAll('path, line, polyline')
|
||
console.log('Found elements for top2:', allPaths.length)
|
||
|
||
let animatedCount = 0
|
||
|
||
allPaths.forEach((element, index) => {
|
||
const el = element as SVGPathElement | SVGLineElement | SVGPolylineElement
|
||
|
||
// 检查是否有描边
|
||
const hasStroke = el.getAttribute('stroke') && el.getAttribute('stroke') !== 'none'
|
||
const hasFill = el.getAttribute('fill') && el.getAttribute('fill') !== 'none'
|
||
|
||
// 只处理有描边且填充为none或没有填充的元素(这些是连线)
|
||
if (!hasStroke || (hasFill && el.getAttribute('fill') !== 'transparent')) {
|
||
return
|
||
}
|
||
|
||
const strokeWidth = parseFloat(el.getAttribute('stroke-width') || '1')
|
||
if (strokeWidth < 0.5) return
|
||
|
||
animatedCount++
|
||
|
||
// 保存原始描边颜色和宽度
|
||
const originalStroke = el.getAttribute('stroke') || '#595959'
|
||
const originalWidth = strokeWidth
|
||
|
||
// 判断是否为特殊颜色线条
|
||
const isBlue = originalStroke.toLowerCase().includes('blue') ||
|
||
originalStroke.toLowerCase().includes('#878fd3') ||
|
||
originalStroke.toLowerCase().includes('#3469f1')
|
||
|
||
const isRed = originalStroke.toLowerCase().includes('red') ||
|
||
originalStroke.toLowerCase().includes('#eb') ||
|
||
originalStroke.toLowerCase().includes('#cf0e30')
|
||
|
||
// 使用更鲜艳的科技感配色方案
|
||
let flowColor = '#00F5FF' // 亮青色(强科技感)
|
||
let glowColor = '#00F5FF'
|
||
let shadowColor = 'rgba(0, 245, 255, 0.6)'
|
||
|
||
if (isBlue) {
|
||
flowColor = '#00D4FF' // 明亮蓝
|
||
glowColor = '#4D9FFF'
|
||
shadowColor = 'rgba(0, 212, 255, 0.6)'
|
||
}
|
||
if (isRed) {
|
||
flowColor = '#FF1744' // 鲜艳红(警告色)
|
||
glowColor = '#FF4081'
|
||
shadowColor = 'rgba(255, 23, 68, 0.6)'
|
||
}
|
||
|
||
// 创建流动层(克隆原线条)
|
||
const flowLine = el.cloneNode(true) as SVGElement
|
||
flowLine.setAttribute('stroke', flowColor)
|
||
flowLine.setAttribute('stroke-width', String(originalWidth * 2.5))
|
||
flowLine.setAttribute('stroke-linecap', 'round')
|
||
flowLine.setAttribute('stroke-opacity', '1')
|
||
flowLine.setAttribute('fill', 'none')
|
||
flowLine.classList.add('flowing-line-top2')
|
||
flowLine.style.setProperty('--glow-color', glowColor)
|
||
flowLine.style.setProperty('--shadow-color', shadowColor)
|
||
|
||
// 计算路径长度
|
||
let pathLength = 0
|
||
if (flowLine instanceof SVGPathElement) {
|
||
pathLength = flowLine.getTotalLength()
|
||
} else if (flowLine instanceof SVGLineElement) {
|
||
const x1 = parseFloat(flowLine.getAttribute('x1') || '0')
|
||
const y1 = parseFloat(flowLine.getAttribute('y1') || '0')
|
||
const x2 = parseFloat(flowLine.getAttribute('x2') || '0')
|
||
const y2 = parseFloat(flowLine.getAttribute('y2') || '0')
|
||
pathLength = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
|
||
}
|
||
|
||
// 设置虚线动画 - 更明显的流动效果
|
||
const dashLength = 60
|
||
const gapLength = 120
|
||
flowLine.style.strokeDasharray = `${dashLength} ${gapLength}`
|
||
flowLine.style.strokeDashoffset = String(pathLength)
|
||
|
||
// 插入流动层到原线条之后
|
||
el.parentNode?.insertBefore(flowLine, el.nextSibling)
|
||
|
||
// 添加 CSS 动画 - 流畅快速的速度
|
||
const duration = 4 + (index % 3)
|
||
const pulseSpeed = duration * 0.4
|
||
flowLine.style.animation = `flowLineTop2 ${duration}s linear infinite, pulseGlow ${pulseSpeed}s ease-in-out infinite`
|
||
flowLine.style.animationDelay = `${(index * 0.3) % 2}s`
|
||
})
|
||
|
||
console.log('Animated top2 lines:', animatedCount)
|
||
|
||
// 添加 Top2 CSS 动画样式
|
||
if (!document.getElementById('flowing-animation-top2-style')) {
|
||
const style = document.createElement('style')
|
||
style.id = 'flowing-animation-top2-style'
|
||
style.textContent = `
|
||
@keyframes flowLineTop2 {
|
||
0% {
|
||
stroke-dashoffset: 1000;
|
||
}
|
||
100% {
|
||
stroke-dashoffset: -1000;
|
||
}
|
||
}
|
||
|
||
@keyframes pulseGlow {
|
||
0%, 100% {
|
||
filter: drop-shadow(0 0 6px var(--glow-color))
|
||
drop-shadow(0 0 12px var(--glow-color))
|
||
drop-shadow(0 0 18px var(--shadow-color));
|
||
stroke-opacity: 1;
|
||
}
|
||
50% {
|
||
filter: drop-shadow(0 0 12px var(--glow-color))
|
||
drop-shadow(0 0 24px var(--glow-color))
|
||
drop-shadow(0 0 36px var(--shadow-color))
|
||
drop-shadow(0 0 48px var(--shadow-color));
|
||
stroke-opacity: 1;
|
||
}
|
||
}
|
||
|
||
.flowing-line-top2 {
|
||
pointer-events: none;
|
||
mix-blend-mode: screen;
|
||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||
}
|
||
|
||
.flowing-line-top2:hover {
|
||
filter: drop-shadow(0 0 20px var(--glow-color))
|
||
drop-shadow(0 0 40px var(--shadow-color)) !important;
|
||
}
|
||
`
|
||
document.head.appendChild(style)
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style scoped>
|
||
.monitor-container {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 20px;
|
||
padding: 10px;
|
||
position: relative;
|
||
}
|
||
|
||
.style-switcher {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
margin-top: 10px;
|
||
}
|
||
|
||
.style-switcher.is-top2,
|
||
.style-switcher.is-top3 {
|
||
margin-top: 0;
|
||
position: absolute;
|
||
top: 24px;
|
||
right: 24px;
|
||
z-index: 5;
|
||
}
|
||
|
||
.style-label {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: rgba(255, 255, 255, 0.75);
|
||
}
|
||
|
||
.style-select {
|
||
padding: 8px 14px;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
background: rgba(255, 255, 255, 0.12);
|
||
border: 1px solid rgba(255, 255, 255, 0.4);
|
||
border-radius: 10px;
|
||
cursor: pointer;
|
||
backdrop-filter: blur(10px);
|
||
outline: none;
|
||
}
|
||
|
||
.style-select option {
|
||
color: #0e1a3f;
|
||
}
|
||
|
||
.title {
|
||
font-size: 2.5rem;
|
||
font-weight: 700;
|
||
color: white;
|
||
text-align: center;
|
||
text-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
|
||
letter-spacing: 2px;
|
||
padding: 20px 30px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
backdrop-filter: blur(10px);
|
||
border-radius: 20px;
|
||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||
animation: titleGlow 3s ease-in-out infinite;
|
||
width: 100%;
|
||
max-width: 1400px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
@keyframes titleGlow {
|
||
0%, 100% {
|
||
box-shadow: 0 0 20px rgba(255, 255, 255, 0.3);
|
||
}
|
||
50% {
|
||
box-shadow: 0 0 40px rgba(255, 255, 255, 0.5);
|
||
}
|
||
}
|
||
|
||
.svg-wrapper {
|
||
flex: 1;
|
||
width: 100%;
|
||
max-width: 1400px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: rgba(255, 255, 255, 0.95);
|
||
border-radius: 20px;
|
||
padding: 30px;
|
||
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
|
||
overflow: auto;
|
||
min-height: 0;
|
||
box-sizing: border-box;
|
||
-webkit-overflow-scrolling: touch;
|
||
}
|
||
|
||
.monitor-container.is-top2,
|
||
.monitor-container.is-top3 {
|
||
padding: 0;
|
||
gap: 0;
|
||
align-items: stretch;
|
||
justify-content: stretch;
|
||
background: #182b69;
|
||
}
|
||
|
||
.top2-stage {
|
||
flex: 1;
|
||
width: 100%;
|
||
overflow: hidden;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #182b69;
|
||
}
|
||
|
||
.top2-scale {
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.top2-layout {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
background: #182b69;
|
||
border-radius: 0;
|
||
overflow: hidden;
|
||
transform: scale(var(--top2-scale));
|
||
transform-origin: top left;
|
||
}
|
||
|
||
.layout-top2 .top2-scale {
|
||
width: calc(1296px * var(--top2-scale));
|
||
height: calc(692px * var(--top2-scale));
|
||
}
|
||
|
||
.layout-top3 .top2-scale {
|
||
width: calc(1438px * var(--top2-scale));
|
||
height: calc(929px * var(--top2-scale));
|
||
}
|
||
|
||
.layout-top2 .top2-layout {
|
||
width: 1296px;
|
||
height: 692px;
|
||
}
|
||
|
||
.layout-top3 .top2-layout {
|
||
width: 1438px;
|
||
height: 929px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.top2-header {
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.layout-top2 .top2-header {
|
||
position: absolute;
|
||
left: 16px;
|
||
top: 16px;
|
||
width: 1264px;
|
||
height: 50px;
|
||
border-radius: 16px;
|
||
background: #e5e5e5;
|
||
border: 0.297px solid #9fbaff;
|
||
}
|
||
|
||
.layout-top3 .top2-header {
|
||
height: 72px;
|
||
border-radius: 0;
|
||
background: transparent;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-image: var(--header-back);
|
||
background-repeat: no-repeat;
|
||
background-position: center;
|
||
background-size: 1120px 72px;
|
||
}
|
||
|
||
.top2-title {
|
||
font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.layout-top2 .top2-title {
|
||
position: absolute;
|
||
left: 516.1px;
|
||
top: 7.78px;
|
||
font-size: 28.491px;
|
||
color: #182b69;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
.layout-top3 .top2-title {
|
||
font-size: 32px;
|
||
color: #f9d1b0;
|
||
letter-spacing: 0.5px;
|
||
text-align: center;
|
||
}
|
||
|
||
.layout-top2 .top2-content {
|
||
position: absolute;
|
||
left: 16px;
|
||
top: 82px;
|
||
width: 1264px;
|
||
height: 594px;
|
||
}
|
||
|
||
.layout-top3 .top2-content {
|
||
width: 100%;
|
||
height: 809px;
|
||
display: flex;
|
||
gap: 16px;
|
||
}
|
||
|
||
.layout-top2 .top2-left-col {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 26.055px;
|
||
width: 232.377px;
|
||
height: 541.889px;
|
||
}
|
||
|
||
.layout-top3 .top2-left-col {
|
||
width: 315px;
|
||
height: 809px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.layout-top2 .top2-center-col {
|
||
position: absolute;
|
||
left: 248.377px;
|
||
top: 25.15px;
|
||
width: 744.126px;
|
||
height: 543.699px;
|
||
padding: 0;
|
||
}
|
||
|
||
.layout-top3 .top2-center-col {
|
||
width: 744px;
|
||
height: 809px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
padding: 0;
|
||
}
|
||
|
||
.layout-top2 .top2-right-col {
|
||
position: absolute;
|
||
left: 1008.503px;
|
||
top: 25.907px;
|
||
width: 232.377px;
|
||
height: 542.186px;
|
||
}
|
||
|
||
.layout-top3 .top2-right-col {
|
||
width: 315px;
|
||
height: 809px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 16px;
|
||
}
|
||
|
||
.top3-board {
|
||
width: 315px;
|
||
height: 396.5px;
|
||
border-radius: 16px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.top3-board-title {
|
||
font-family: 'Inter', 'Noto Sans JP', sans-serif;
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #f02525;
|
||
text-align: center;
|
||
line-height: 40px;
|
||
}
|
||
|
||
.top3-board-table {
|
||
width: 100%;
|
||
height: 156px;
|
||
display: flex;
|
||
}
|
||
|
||
.top3-board-col {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.top3-rank-col {
|
||
width: 36px;
|
||
}
|
||
|
||
.top3-info-col {
|
||
width: 146px;
|
||
}
|
||
|
||
.top3-metric-col {
|
||
width: 133px;
|
||
}
|
||
|
||
.top3-table-head {
|
||
height: 40px;
|
||
background: #043272;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 16px;
|
||
color: rgba(255, 255, 255, 0.75);
|
||
}
|
||
|
||
.top3-table-head-left {
|
||
justify-content: flex-start;
|
||
padding: 0 10px;
|
||
}
|
||
|
||
.top3-table-head-right {
|
||
justify-content: flex-start;
|
||
padding: 0 0 0 16px;
|
||
text-align: left;
|
||
}
|
||
|
||
.top3-table-cell {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
}
|
||
|
||
.top3-rank-cell {
|
||
justify-content: center;
|
||
}
|
||
|
||
.top3-info-cell {
|
||
padding: 0 10px;
|
||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 18px;
|
||
color: #ffffff;
|
||
line-height: 28px;
|
||
}
|
||
|
||
.top3-metric-cell {
|
||
justify-content: flex-start;
|
||
padding: 0 0 0 16px;
|
||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 20px;
|
||
color: #4de4ff;
|
||
}
|
||
|
||
.rank-badge {
|
||
width: 24px;
|
||
height: 24px;
|
||
border-radius: 2px;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-family: 'Mark Pro', 'Inter', sans-serif;
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
letter-spacing: 0.75px;
|
||
}
|
||
|
||
.rank-1 {
|
||
background: #c85b3f;
|
||
}
|
||
|
||
.rank-2 {
|
||
background: #b99a3a;
|
||
}
|
||
|
||
.rank-3 {
|
||
background: #1fb2a4;
|
||
}
|
||
|
||
.top3-board-bottom {
|
||
width: 100%;
|
||
height: 180.5px;
|
||
background: #ffffff;
|
||
border-bottom-left-radius: 16px;
|
||
border-bottom-right-radius: 16px;
|
||
position: relative;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.top3-bottom-title {
|
||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: rgba(0, 0, 0, 0.6);
|
||
position: absolute;
|
||
left: 13.03px;
|
||
top: 15.427px;
|
||
}
|
||
|
||
.top3-bottom-line {
|
||
width: 246.568px;
|
||
height: 1.556px;
|
||
background: rgba(240, 226, 226, 0.9);
|
||
position: absolute;
|
||
left: 34.759px;
|
||
}
|
||
|
||
.top3-board-bottom .top3-bottom-line:nth-of-type(2) {
|
||
top: 72.508px;
|
||
left: 35.845px;
|
||
}
|
||
|
||
.top3-board-bottom .top3-bottom-line:nth-of-type(3) {
|
||
top: 109.548px;
|
||
}
|
||
|
||
.top3-board-bottom .top3-bottom-line:nth-of-type(4) {
|
||
top: 146.573px;
|
||
}
|
||
|
||
.panel-outline {
|
||
border: 0.297px solid #f0e2e2;
|
||
border-radius: 10.684px;
|
||
background: rgba(217, 217, 217, 0);
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.layout-top2 .panel-outline {
|
||
position: absolute;
|
||
}
|
||
|
||
.layout-top3 .panel-outline {
|
||
position: relative;
|
||
}
|
||
|
||
.top3-board.panel-outline {
|
||
border-radius: 16px;
|
||
}
|
||
|
||
.layout-top2 .panel-left-top {
|
||
left: 0;
|
||
top: 0;
|
||
width: 232.377px;
|
||
height: 259.977px;
|
||
}
|
||
|
||
.layout-top3 .panel-left-top {
|
||
width: 315px;
|
||
height: 259px;
|
||
}
|
||
|
||
.layout-top3 .panel-left-middle {
|
||
width: 315px;
|
||
height: 259px;
|
||
}
|
||
|
||
.layout-top2 .panel-left-bottom {
|
||
left: 0;
|
||
top: 275.977px;
|
||
width: 232.377px;
|
||
height: 265.912px;
|
||
}
|
||
|
||
.layout-top3 .panel-left-bottom {
|
||
width: 315px;
|
||
height: 259px;
|
||
}
|
||
|
||
.layout-top2 .panel-right-top {
|
||
left: 0;
|
||
top: 0;
|
||
width: 232.377px;
|
||
height: 259.977px;
|
||
}
|
||
|
||
.layout-top3 .panel-right-top {
|
||
width: 315px;
|
||
height: 359px;
|
||
}
|
||
|
||
.layout-top2 .panel-right-bottom {
|
||
left: 0;
|
||
top: 275.977px;
|
||
width: 232.377px;
|
||
height: 266.209px;
|
||
}
|
||
|
||
.layout-top3 .panel-right-bottom {
|
||
width: 315px;
|
||
height: 359px;
|
||
}
|
||
|
||
.layout-top2 .panel-center-bottom-left {
|
||
left: 0;
|
||
top: 0;
|
||
width: 360.668px;
|
||
height: 175.692px;
|
||
}
|
||
|
||
.layout-top2 .panel-center-bottom-right {
|
||
left: 386.668px;
|
||
top: 0;
|
||
width: 357.216px;
|
||
height: 175.692px;
|
||
}
|
||
|
||
.layout-top3 .panel-center-bottom-left {
|
||
width: auto;
|
||
flex: 1;
|
||
height: 242.813px;
|
||
}
|
||
|
||
.layout-top3 .panel-center-bottom-right {
|
||
width: auto;
|
||
flex: 1;
|
||
height: 242.813px;
|
||
}
|
||
|
||
.layout-top2 .top2-center {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
width: 743.928px;
|
||
height: 352.007px;
|
||
}
|
||
|
||
.layout-top3 .top2-center {
|
||
width: 696.481px;
|
||
height: 333.004px;
|
||
position: relative;
|
||
overflow: visible;
|
||
}
|
||
|
||
.layout-top2 .top2-bottom-row {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 368.007px;
|
||
width: 744.126px;
|
||
height: 175.692px;
|
||
}
|
||
|
||
.layout-top3 .top2-bottom-row {
|
||
width: 744px;
|
||
height: 242.818px;
|
||
display: flex;
|
||
gap: 16px;
|
||
align-items: stretch;
|
||
}
|
||
|
||
.top2-cards {
|
||
height: 114px;
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
padding: 8px 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.top2-card {
|
||
width: 180.032px;
|
||
height: 98px;
|
||
border-radius: 16px;
|
||
background: rgba(37, 67, 165, 0.51);
|
||
display: flex;
|
||
gap: 12px;
|
||
align-items: center;
|
||
padding: 8px;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.top2-card-icon {
|
||
width: 51px;
|
||
height: 49px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
}
|
||
|
||
.top2-card-icon::before {
|
||
content: '';
|
||
position: absolute;
|
||
width: 49px;
|
||
height: 49px;
|
||
border-radius: 50%;
|
||
border: 1.5px solid rgba(255, 255, 255, 0.7);
|
||
background: radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.35), rgba(37, 67, 165, 0.15) 60%, rgba(37, 67, 165, 0) 100%);
|
||
box-shadow: 0 0 10px rgba(0, 245, 255, 0.35);
|
||
}
|
||
|
||
.top2-card-icon img {
|
||
width: 28px;
|
||
height: 28px;
|
||
display: block;
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.top2-card-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4px;
|
||
}
|
||
|
||
.top2-card-label {
|
||
font-family: 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.top2-card-value {
|
||
font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
line-height: 1;
|
||
}
|
||
|
||
.top2-card-sub {
|
||
font-family: 'Inter', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 12px;
|
||
font-weight: 700;
|
||
color: rgba(191, 191, 191, 0.61);
|
||
}
|
||
|
||
.layout-top2 .top2-cards,
|
||
.layout-top2 .top2-subheader {
|
||
display: none;
|
||
}
|
||
|
||
.layout-top3 .top2-cards {
|
||
width: 744px;
|
||
}
|
||
|
||
.top2-subheader {
|
||
height: 51px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.layout-top3 .top2-subheader {
|
||
display: none;
|
||
}
|
||
|
||
.top3-center-block {
|
||
width: 744px;
|
||
height: 436.182px;
|
||
position: relative;
|
||
}
|
||
|
||
.top3-center-block .top2-center {
|
||
position: absolute;
|
||
left: 24.982px;
|
||
top: 65px;
|
||
width: 696.481px;
|
||
height: 333.004px;
|
||
z-index: 1;
|
||
}
|
||
|
||
.top3-center-block .svg-wrapper {
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.top3-map-frame {
|
||
position: absolute;
|
||
inset: 0;
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: fill;
|
||
pointer-events: none;
|
||
z-index: 2;
|
||
}
|
||
|
||
.svg-wrapper.is-top2,
|
||
.svg-wrapper.is-top3 {
|
||
flex: none;
|
||
width: 100%;
|
||
height: 100%;
|
||
max-width: none;
|
||
padding: 0;
|
||
background: transparent;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.layout-top2 .panel-label {
|
||
position: absolute;
|
||
font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 18.994px;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
letter-spacing: 0.2px;
|
||
}
|
||
|
||
.layout-top3 .panel-label {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 20px;
|
||
transform: translate(-50%, -50%);
|
||
font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
letter-spacing: 0.2px;
|
||
text-align: center;
|
||
width: 220px;
|
||
}
|
||
|
||
.layout-top2 .label-metric-11 {
|
||
left: 61.73px;
|
||
top: 91.7px;
|
||
}
|
||
|
||
.layout-top2 .label-metric-21 {
|
||
left: 61.73px;
|
||
top: 121.68px;
|
||
}
|
||
|
||
.layout-top2 .label-metric-22 {
|
||
left: 132.11px;
|
||
top: 76.27px;
|
||
}
|
||
|
||
.layout-top2 .label-metric-23 {
|
||
left: 126.11px;
|
||
top: 76.27px;
|
||
}
|
||
|
||
.layout-top2 .label-alarm {
|
||
left: 78.35px;
|
||
top: 18.7px;
|
||
color: #f02525;
|
||
}
|
||
|
||
.layout-top2 .label-fault {
|
||
left: 78.35px;
|
||
top: 22.85px;
|
||
color: #ffffff;
|
||
}
|
||
|
||
.layout-top3 .label-alarm {
|
||
color: #f02525;
|
||
}
|
||
|
||
.svg-wrapper :deep(svg) {
|
||
width: 100%;
|
||
height: auto;
|
||
max-height: 100%;
|
||
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.1));
|
||
touch-action: pan-x pan-y;
|
||
}
|
||
|
||
.svg-wrapper.is-top2 :deep(svg),
|
||
.svg-wrapper.is-top3 :deep(svg) {
|
||
height: 100%;
|
||
max-height: none;
|
||
filter: none;
|
||
}
|
||
|
||
.svg-wrapper.is-top2 :deep(#shape1),
|
||
.svg-wrapper.is-top3 :deep(#shape1) {
|
||
fill: transparent;
|
||
stroke: none;
|
||
}
|
||
|
||
:deep(.flowing-line) {
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
:deep(.flowing-line:hover) {
|
||
stroke-width: 6 !important;
|
||
filter: drop-shadow(0 0 8px #00ff88);
|
||
}
|
||
|
||
/* 平板适配 */
|
||
@media (max-width: 1024px) {
|
||
.title {
|
||
font-size: 2rem;
|
||
padding: 15px 20px;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.monitor-container:not(.is-top2):not(.is-top3) .svg-wrapper {
|
||
padding: 20px;
|
||
}
|
||
}
|
||
|
||
/* 手机适配 */
|
||
@media (max-width: 768px) {
|
||
.monitor-container:not(.is-top2):not(.is-top3) {
|
||
gap: 15px;
|
||
padding: 10px;
|
||
}
|
||
|
||
.title {
|
||
font-size: 1.5rem;
|
||
padding: 12px 16px;
|
||
letter-spacing: 0.5px;
|
||
border-radius: 15px;
|
||
}
|
||
|
||
.monitor-container:not(.is-top2):not(.is-top3) .svg-wrapper {
|
||
padding: 15px;
|
||
border-radius: 15px;
|
||
}
|
||
}
|
||
|
||
/* 小屏手机适配 */
|
||
@media (max-width: 480px) {
|
||
.monitor-container:not(.is-top2):not(.is-top3) {
|
||
gap: 10px;
|
||
padding: 8px;
|
||
}
|
||
|
||
.title {
|
||
font-size: 1.2rem;
|
||
padding: 10px 12px;
|
||
letter-spacing: 0.3px;
|
||
border-radius: 12px;
|
||
}
|
||
|
||
.monitor-container:not(.is-top2):not(.is-top3) .svg-wrapper {
|
||
padding: 10px;
|
||
border-radius: 12px;
|
||
}
|
||
}
|
||
</style>
|