|
@@ -24,7 +24,7 @@
|
|
|
<div class="weather-cards">
|
|
<div class="weather-cards">
|
|
|
<div class="weather-card" v-for="(w, i) in weatherList" :key="i">
|
|
<div class="weather-card" v-for="(w, i) in weatherList" :key="i">
|
|
|
<div class="weather-date">{{ w.date }}</div>
|
|
<div class="weather-date">{{ w.date }}</div>
|
|
|
- <div class="weather-icon">{{ w.icon }}</div>
|
|
|
|
|
|
|
+ <div class="weather-icon">{{ getWeatherIcon(w.text) }}</div>
|
|
|
<div class="weather-desc">{{ w.desc }}</div>
|
|
<div class="weather-desc">{{ w.desc }}</div>
|
|
|
<div class="weather-temp">{{ w.tempRange }}</div>
|
|
<div class="weather-temp">{{ w.tempRange }}</div>
|
|
|
<div class="weather-wind">{{ w.wind }}</div>
|
|
<div class="weather-wind">{{ w.wind }}</div>
|
|
@@ -37,13 +37,6 @@
|
|
|
<div class="panel-title">
|
|
<div class="panel-title">
|
|
|
<span class="title-icon">▶</span> 监测点概况
|
|
<span class="title-icon">▶</span> 监测点概况
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="monitor-stats-row">
|
|
|
|
|
- <span class="stat-tag online">在线 {{ monitorStats.online }}</span>
|
|
|
|
|
- <span class="stat-divider">|</span>
|
|
|
|
|
- <span class="stat-tag offline">离线 {{ monitorStats.offline }}</span>
|
|
|
|
|
- <span class="stat-divider">|</span>
|
|
|
|
|
- <span class="stat-tag rate">在线率 {{ monitorStats.onlineRate }}%</span>
|
|
|
|
|
- </div>
|
|
|
|
|
<div class="pie-chart-wrapper">
|
|
<div class="pie-chart-wrapper">
|
|
|
<div id="jsqcPieChart" class="chart-area"></div>
|
|
<div id="jsqcPieChart" class="chart-area"></div>
|
|
|
<div class="pie-legend">
|
|
<div class="pie-legend">
|
|
@@ -91,38 +84,11 @@
|
|
|
|
|
|
|
|
<!-- ========== 中间地图区域 ========== -->
|
|
<!-- ========== 中间地图区域 ========== -->
|
|
|
<div class="panel-center">
|
|
<div class="panel-center">
|
|
|
- <div class="map-search">
|
|
|
|
|
- <input type="text" placeholder="输入水源地" class="search-input" />
|
|
|
|
|
- <span class="search-icon">🔍</span>
|
|
|
|
|
- </div>
|
|
|
|
|
<div id="jsqcMap" class="map-container"></div>
|
|
<div id="jsqcMap" class="map-container"></div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- ========== 右侧面板 ========== -->
|
|
<!-- ========== 右侧面板 ========== -->
|
|
|
<div class="panel-right">
|
|
<div class="panel-right">
|
|
|
- <!-- 积水分析 -->
|
|
|
|
|
- <div class="panel-box">
|
|
|
|
|
- <div class="panel-title">
|
|
|
|
|
- <span class="title-icon">▶</span> 积水分析
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="analysis-filter">
|
|
|
|
|
- <select class="dark-select" v-model="analysisYear">
|
|
|
|
|
- <option v-for="y in yearOptions" :key="y" :value="y">{{ y }}年</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- <select class="dark-select" v-model="analysisLevel">
|
|
|
|
|
- <option value="严重积水">严重积水</option>
|
|
|
|
|
- <option value="中度积水">中度积水</option>
|
|
|
|
|
- <option value="轻度积水">轻度积水</option>
|
|
|
|
|
- <option value="未积水">未积水</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- <button class="analysis-btn" @click="startAnalysis">开始分析</button>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="analysis-result" v-if="analysisResult">
|
|
|
|
|
- {{ analysisResult }}
|
|
|
|
|
- </div>
|
|
|
|
|
- <div id="jsqcBarChart" class="chart-area-sm"></div>
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
<!-- 积水统计 -->
|
|
<!-- 积水统计 -->
|
|
|
<div class="panel-box">
|
|
<div class="panel-box">
|
|
|
<div class="panel-title">
|
|
<div class="panel-title">
|
|
@@ -130,59 +96,38 @@
|
|
|
</div>
|
|
</div>
|
|
|
<div id="jsqcLineChart" class="chart-area-sm"></div>
|
|
<div id="jsqcLineChart" class="chart-area-sm"></div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <!-- 预警信息 -->
|
|
|
|
|
- <div class="panel-box">
|
|
|
|
|
- <div class="panel-title">
|
|
|
|
|
- <span class="title-icon">▶</span> 预警信息
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="alert-stats">
|
|
|
|
|
- <div class="alert-stat-card">
|
|
|
|
|
- <div class="alert-stat-val">{{ alertStats.warning }}</div>
|
|
|
|
|
- <div class="alert-stat-label">积水预警数</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="alert-stat-card">
|
|
|
|
|
- <div class="alert-stat-val green">{{ alertStats.online }}</div>
|
|
|
|
|
- <div class="alert-stat-label">设备在线数</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="alert-stat-card">
|
|
|
|
|
- <div class="alert-stat-val red">{{ alertStats.offline }}</div>
|
|
|
|
|
- <div class="alert-stat-label">设备离线数</div>
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="alert-table-wrap">
|
|
|
|
|
- <table class="dark-table">
|
|
|
|
|
- <thead>
|
|
|
|
|
- <tr>
|
|
|
|
|
- <th>序号</th>
|
|
|
|
|
- <th>地址</th>
|
|
|
|
|
- <th>时间</th>
|
|
|
|
|
- <th>报警类型</th>
|
|
|
|
|
- <th>实时数据</th>
|
|
|
|
|
- </tr>
|
|
|
|
|
- </thead>
|
|
|
|
|
- <tbody>
|
|
|
|
|
- <tr v-for="(row, i) in alertList" :key="i">
|
|
|
|
|
- <td>{{ i + 1 }}</td>
|
|
|
|
|
- <td :title="row.address">{{ row.address }}</td>
|
|
|
|
|
- <td>{{ row.time }}</td>
|
|
|
|
|
- <td>{{ row.type }}</td>
|
|
|
|
|
- <td>{{ row.data }}</td>
|
|
|
|
|
- </tr>
|
|
|
|
|
- </tbody>
|
|
|
|
|
- </table>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <!-- 积水深度曲线弹窗 -->
|
|
|
|
|
+ <div v-if="depthModalVisible" class="depth-modal" @click.self="closeDepthModal">
|
|
|
|
|
+ <div class="depth-modal-content">
|
|
|
|
|
+ <div class="depth-modal-header">
|
|
|
|
|
+ <span class="depth-modal-title">{{ selectedPointName }} - 积水深度曲线</span>
|
|
|
|
|
+ <span class="depth-modal-close" @click="closeDepthModal">×</span>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="depth-modal-tabs">
|
|
|
|
|
+ <div
|
|
|
|
|
+ v-for="tab in depthTabs"
|
|
|
|
|
+ :key="tab.value"
|
|
|
|
|
+ class="depth-modal-tab"
|
|
|
|
|
+ :class="{ active: currentDepthTab === tab.value }"
|
|
|
|
|
+ @click="switchDepthTab(tab.value)"
|
|
|
|
|
+ >{{ tab.label }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div id="jsqcDepthChart" class="depth-chart-area"></div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup name="Jsqc">
|
|
<script setup name="Jsqc">
|
|
|
-import { ref, reactive, onMounted, onUnmounted } from 'vue'
|
|
|
|
|
|
|
+import { ref, onMounted, onUnmounted, nextTick } from 'vue'
|
|
|
import * as echarts from 'echarts'
|
|
import * as echarts from 'echarts'
|
|
|
import { useRouter } from 'vue-router'
|
|
import { useRouter } from 'vue-router'
|
|
|
|
|
+import { ElMessage } from 'element-plus'
|
|
|
import locationIcon from '@/assets/images/location.png'
|
|
import locationIcon from '@/assets/images/location.png'
|
|
|
|
|
+import { getWaterloggingPage } from '@/api/drainage'
|
|
|
|
|
|
|
|
const router = useRouter()
|
|
const router = useRouter()
|
|
|
|
|
|
|
@@ -207,75 +152,204 @@ function updateRefreshTime() {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ==================== 天气数据 ====================
|
|
// ==================== 天气数据 ====================
|
|
|
-const weatherList = ref([
|
|
|
|
|
- { date: '06/11', icon: '☁️', desc: '多云', tempRange: '18°C ~ 26°C', wind: '南风 2级' },
|
|
|
|
|
- { date: '06/12', icon: '🌧️', desc: '小雨', tempRange: '16°C ~ 23°C', wind: '东风 3级' },
|
|
|
|
|
- { date: '06/13', icon: '⛅', desc: '阴转多云', tempRange: '15°C ~ 24°C', wind: '北风 2级' }
|
|
|
|
|
-])
|
|
|
|
|
|
|
+const weatherList = ref([])
|
|
|
|
|
+
|
|
|
|
|
+// 百度地图 AK(与 index.html 中加载百度地图 SDK 使用同一个)
|
|
|
|
|
+const BAIDU_MAP_AK = 'vMma3LYQQyrfhlFUm0CFzXFQhBrwxAmi'
|
|
|
|
|
+
|
|
|
|
|
+// 天气文字 -> emoji 图标映射
|
|
|
|
|
+function getWeatherIcon(text) {
|
|
|
|
|
+ if (!text) return '☀️'
|
|
|
|
|
+ const t = String(text)
|
|
|
|
|
+ if (t.includes('晴')) return '☀️'
|
|
|
|
|
+ if (t.includes('多云')) return '⛅'
|
|
|
|
|
+ if (t.includes('阴')) return '☁️'
|
|
|
|
|
+ if (t.includes('雨') && t.includes('雪')) return '🌨️'
|
|
|
|
|
+ if (t.includes('雪')) return '❄️'
|
|
|
|
|
+ if (t.includes('雷')) return '⛈️'
|
|
|
|
|
+ if (t.includes('雨')) return '🌧️'
|
|
|
|
|
+ if (t.includes('雾') || t.includes('霾')) return '🌫️'
|
|
|
|
|
+ return '☁️'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 简易 JSONP 调用百度天气服务(服务端未开放 CORS,需用 JSONP)
|
|
|
|
|
+function jsonpRequest(url) {
|
|
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
|
|
+ const callbackName = 'jsqcWeatherCallback_' + Date.now()
|
|
|
|
|
+ const script = document.createElement('script')
|
|
|
|
|
+ script.src = url + (url.includes('?') ? '&' : '?') + 'callback=' + callbackName
|
|
|
|
|
+ script.onerror = () => {
|
|
|
|
|
+ document.head.removeChild(script)
|
|
|
|
|
+ delete window[callbackName]
|
|
|
|
|
+ reject(new Error('天气接口加载失败'))
|
|
|
|
|
+ }
|
|
|
|
|
+ window[callbackName] = (data) => {
|
|
|
|
|
+ document.head.removeChild(script)
|
|
|
|
|
+ delete window[callbackName]
|
|
|
|
|
+ resolve(data)
|
|
|
|
|
+ }
|
|
|
|
|
+ document.head.appendChild(script)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
-// ==================== 监测点概况 ====================
|
|
|
|
|
-const monitorStats = reactive({ online: 15, offline: 0, onlineRate: 100 })
|
|
|
|
|
|
|
+async function loadWeatherData() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const url = `https://api.map.baidu.com/weather/v1/?district_id=500156&data_type=all&ak=${BAIDU_MAP_AK}&output=json`
|
|
|
|
|
+ const res = await jsonpRequest(url)
|
|
|
|
|
+ if (res && (res.status === 0 || res.status === '0') && res.result && Array.isArray(res.result.forecasts)) {
|
|
|
|
|
+ weatherList.value = res.result.forecasts.slice(0, 3).map(day => {
|
|
|
|
|
+ const dateStr = day.date || ''
|
|
|
|
|
+ const monthDay = dateStr.length >= 10 ? `${dateStr.slice(5, 7)}/${dateStr.slice(8, 10)}` : dateStr
|
|
|
|
|
+ return {
|
|
|
|
|
+ date: monthDay,
|
|
|
|
|
+ text: day.text_day || day.text_night || '多云',
|
|
|
|
|
+ desc: day.text_day || '多云',
|
|
|
|
|
+ tempRange: `${day.low || '--'}°C ~ ${day.high || '--'}°C`,
|
|
|
|
|
+ wind: `${day.wd_day || ''} ${day.wc_day || ''}`
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.warn('百度天气返回异常:', res)
|
|
|
|
|
+ useMockWeather()
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('加载天气数据失败:', e)
|
|
|
|
|
+ useMockWeather()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 天气接口失败时使用模拟数据兜底
|
|
|
|
|
+function useMockWeather() {
|
|
|
|
|
+ const now = new Date()
|
|
|
|
|
+ const pad = n => String(n).padStart(2, '0')
|
|
|
|
|
+ const formatDate = (offset) => {
|
|
|
|
|
+ const d = new Date(now.getTime() + offset * 24 * 60 * 60 * 1000)
|
|
|
|
|
+ return `${pad(d.getMonth() + 1)}/${pad(d.getDate())}`
|
|
|
|
|
+ }
|
|
|
|
|
+ weatherList.value = [
|
|
|
|
|
+ { date: formatDate(0), text: '多云', desc: '多云', tempRange: '18°C ~ 26°C', wind: '东南风 2级' },
|
|
|
|
|
+ { date: formatDate(1), text: '小雨', desc: '小雨', tempRange: '16°C ~ 23°C', wind: '东风 3级' },
|
|
|
|
|
+ { date: formatDate(2), text: '阴', desc: '阴', tempRange: '15°C ~ 22°C', wind: '北风 2级' }
|
|
|
|
|
+ ]
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
|
|
+// ==================== 监测点概况 ====================
|
|
|
const pieData = ref([
|
|
const pieData = ref([
|
|
|
- { name: '未积水', value: 10, color: '#67c23a' },
|
|
|
|
|
- { name: '轻度积水', value: 3, color: '#409eff' },
|
|
|
|
|
- { name: '中度积水', value: 1, color: '#e6a23c' },
|
|
|
|
|
- { name: '严重积水', value: 1, color: '#f56c6c' }
|
|
|
|
|
|
|
+ { name: '未积水', value: 0, color: '#67c23a' },
|
|
|
|
|
+ { name: '轻度积水', value: 0, color: '#409eff' },
|
|
|
|
|
+ { name: '中度积水', value: 0, color: '#e6a23c' },
|
|
|
|
|
+ { name: '严重积水', value: 0, color: '#f56c6c' }
|
|
|
])
|
|
])
|
|
|
|
|
|
|
|
// ==================== 实时数据 ====================
|
|
// ==================== 实时数据 ====================
|
|
|
-const realtimeData = ref([
|
|
|
|
|
- { name: '沅陵大道1号闸口', time: '08:32:15', depth: 0, level: '未积水', levelClass: 'none' },
|
|
|
|
|
- { name: '城北路段排水井A3', time: '08:30:42', depth: 3.2, level: '轻度积水', levelClass: 'light' },
|
|
|
|
|
- { name: '河西大道排水管道B7', time: '08:31:08', depth: 8.5, level: '轻度积水', levelClass: 'light' },
|
|
|
|
|
- { name: '人民路地下管网C2', time: '08:29:55', depth: 15.3, level: '中度积水', levelClass: 'moderate' },
|
|
|
|
|
- { name: '南环路排水管道D3', time: '08:33:01', depth: 0, level: '未积水', levelClass: 'none' },
|
|
|
|
|
- { name: '北环路排水泵站出口', time: '08:28:47', depth: 5.1, level: '轻度积水', levelClass: 'light' },
|
|
|
|
|
- { name: '西环路河道排放口', time: '08:31:55', depth: 28.6, level: '严重积水', levelClass: 'severe' },
|
|
|
|
|
- { name: '东环路市政大楼段', time: '08:32:40', depth: 0, level: '未积水', levelClass: 'none' },
|
|
|
|
|
- { name: '开发区管委会路段', time: '08:30:22', depth: 12.4, level: '中度积水', levelClass: 'moderate' },
|
|
|
|
|
- { name: '城北二号排水井A7', time: '08:33:18', depth: 0, level: '未积水', levelClass: 'none' }
|
|
|
|
|
-])
|
|
|
|
|
|
|
+const realtimeData = ref([])
|
|
|
|
|
|
|
|
-// ==================== 积水分析 ====================
|
|
|
|
|
-const yearOptions = [2026, 2025, 2024, 2023]
|
|
|
|
|
-const analysisYear = ref(2025)
|
|
|
|
|
-const analysisLevel = ref('严重积水')
|
|
|
|
|
-const analysisResult = ref('')
|
|
|
|
|
-
|
|
|
|
|
-function startAnalysis() {
|
|
|
|
|
- analysisResult.value = `沅陵县在${analysisYear.value}年,按${analysisLevel.value}次数进行统计,积水次数最多的监测点是河西大道排水管道B7号,${analysisLevel.value} 10 次。${analysisYear.value}-03-05 19:39:58 积水最深,积水深度达 0.34 m。`
|
|
|
|
|
|
|
+// ==================== 地图监测点数据 ====================
|
|
|
|
|
+const mapPoints = ref([])
|
|
|
|
|
+
|
|
|
|
|
+// ==================== 积水等级映射 ====================
|
|
|
|
|
+// 后端 waterloggingLevel 约定:0=未积水,1=轻度,2=中度,3=严重
|
|
|
|
|
+function getWaterloggingLevelInfo(level) {
|
|
|
|
|
+ const map = {
|
|
|
|
|
+ 0: { label: '未积水', levelClass: 'none' },
|
|
|
|
|
+ 1: { label: '轻度积水', levelClass: 'light' },
|
|
|
|
|
+ 2: { label: '中度积水', levelClass: 'moderate' },
|
|
|
|
|
+ 3: { label: '严重积水', levelClass: 'severe' }
|
|
|
|
|
+ }
|
|
|
|
|
+ return map[level] || map[Number(level)] || { label: '未积水', levelClass: 'none' }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 安全解析 waterloggingPoints(后端返回 JSON 字符串,需转为数组使用)
|
|
|
|
|
+function parseWaterloggingPoints(raw) {
|
|
|
|
|
+ if (!raw) return []
|
|
|
|
|
+ if (Array.isArray(raw)) return raw
|
|
|
|
|
+ if (typeof raw === 'string') {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const parsed = JSON.parse(raw)
|
|
|
|
|
+ return Array.isArray(parsed) ? parsed : []
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.warn('waterloggingPoints JSON 解析失败:', raw, e)
|
|
|
|
|
+ return []
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return []
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// ==================== 预警信息 ====================
|
|
|
|
|
-const alertStats = reactive({ warning: 3, online: 12, offline: 0 })
|
|
|
|
|
-
|
|
|
|
|
-const alertList = ref([
|
|
|
|
|
- { address: '沅陵县河西大道', time: '06-11 08:32', type: '降雨预警', data: '大雨' },
|
|
|
|
|
- { address: '沅陵县城北路段', time: '06-11 08:30', type: '积水预警', data: '12.4cm' },
|
|
|
|
|
- { address: '沅陵县西环路', time: '06-11 08:28', type: '积水预警', data: '28.6cm' },
|
|
|
|
|
- { address: '沅陵县南环路', time: '06-11 07:55', type: '降雨预警', data: '中雨' },
|
|
|
|
|
- { address: '沅陵县人民路', time: '06-11 07:42', type: '积水预警', data: '15.3cm' }
|
|
|
|
|
-])
|
|
|
|
|
|
|
+// ==================== API数据加载 ====================
|
|
|
|
|
+async function loadWaterloggingData() {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const res = await getWaterloggingPage(1, 999)
|
|
|
|
|
+ // 兼容直接返回 Page 对象(无 code 包装)和 AjaxResult 包装两种格式
|
|
|
|
|
+ const pageData = res && res.code !== undefined ? res.data : res
|
|
|
|
|
+ const list = Array.isArray(pageData.records) ? pageData.records : (Array.isArray(pageData) ? pageData : [])
|
|
|
|
|
+
|
|
|
|
|
+ // 构建地图点位:展示每个易积水点的所有子点位
|
|
|
|
|
+ const allMapPoints = []
|
|
|
|
|
+ list.forEach(p => {
|
|
|
|
|
+ const levelInfo = getWaterloggingLevelInfo(p.waterloggingLevel)
|
|
|
|
|
+ const subPoints = parseWaterloggingPoints(p.waterloggingPoints)
|
|
|
|
|
+ if (subPoints.length > 0) {
|
|
|
|
|
+ subPoints.forEach(sp => {
|
|
|
|
|
+ if (sp.lng && sp.lat) {
|
|
|
|
|
+ allMapPoints.push({
|
|
|
|
|
+ name: sp.name || p.pointName || '未命名',
|
|
|
|
|
+ lng: Number(sp.lng),
|
|
|
|
|
+ lat: Number(sp.lat),
|
|
|
|
|
+ depth: sp.depth ?? p.waterVolume ?? 0,
|
|
|
|
|
+ level: levelInfo.label,
|
|
|
|
|
+ levelClass: levelInfo.levelClass,
|
|
|
|
|
+ parentId: p.id,
|
|
|
|
|
+ parentName: p.pointName
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ } else if (p.longitude && p.latitude) {
|
|
|
|
|
+ // 兼容无子点位的旧数据:使用主点坐标
|
|
|
|
|
+ allMapPoints.push({
|
|
|
|
|
+ name: p.pointName || '未命名',
|
|
|
|
|
+ lng: Number(p.longitude),
|
|
|
|
|
+ lat: Number(p.latitude),
|
|
|
|
|
+ depth: p.waterVolume ?? 0,
|
|
|
|
|
+ level: levelInfo.label,
|
|
|
|
|
+ levelClass: levelInfo.levelClass,
|
|
|
|
|
+ parentId: p.id,
|
|
|
|
|
+ parentName: p.pointName
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ mapPoints.value = allMapPoints
|
|
|
|
|
+
|
|
|
|
|
+ // 构建实时数据表(按子点位展开)
|
|
|
|
|
+ const pad = n => String(n).padStart(2, '0')
|
|
|
|
|
+ const now = new Date()
|
|
|
|
|
+ const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`
|
|
|
|
|
+ realtimeData.value = allMapPoints.map((p, idx) => ({
|
|
|
|
|
+ name: p.name,
|
|
|
|
|
+ time: timeStr,
|
|
|
|
|
+ depth: p.depth,
|
|
|
|
|
+ level: p.level,
|
|
|
|
|
+ levelClass: p.levelClass
|
|
|
|
|
+ })).slice(0, 50)
|
|
|
|
|
+
|
|
|
|
|
+ // 更新 pieData 统计(按 waterloggingLevel 统计各等级数量)
|
|
|
|
|
+ const levelCount = { 0: 0, 1: 0, 2: 0, 3: 0 }
|
|
|
|
|
+ list.forEach(p => {
|
|
|
|
|
+ const key = Number(p.waterloggingLevel ?? 0)
|
|
|
|
|
+ if (levelCount[key] !== undefined) levelCount[key]++
|
|
|
|
|
+ else levelCount[0]++
|
|
|
|
|
+ })
|
|
|
|
|
+ pieData.value = [
|
|
|
|
|
+ { name: '未积水', value: levelCount[0], color: '#67c23a' },
|
|
|
|
|
+ { name: '轻度积水', value: levelCount[1], color: '#409eff' },
|
|
|
|
|
+ { name: '中度积水', value: levelCount[2], color: '#e6a23c' },
|
|
|
|
|
+ { name: '严重积水', value: levelCount[3], color: '#f56c6c' }
|
|
|
|
|
+ ]
|
|
|
|
|
|
|
|
-// ==================== 地图监测点数据 ====================
|
|
|
|
|
-const mapPoints = ref([
|
|
|
|
|
- { name: '沅陵大道1号闸口', lng: 110.393, lat: 28.452, depth: 0, level: '未积水' },
|
|
|
|
|
- { name: '城北路段排水井A3', lng: 110.385, lat: 28.462, depth: 3.2, level: '轻度积水' },
|
|
|
|
|
- { name: '河西大道排水管道B7', lng: 110.378, lat: 28.456, depth: 8.5, level: '轻度积水' },
|
|
|
|
|
- { name: '人民路地下管网C2', lng: 110.396, lat: 28.451, depth: 15.3, level: '中度积水' },
|
|
|
|
|
- { name: '南环路排水管道D3', lng: 110.402, lat: 28.444, depth: 0, level: '未积水' },
|
|
|
|
|
- { name: '北环路排水泵站出口', lng: 110.401, lat: 28.460, depth: 5.1, level: '轻度积水' },
|
|
|
|
|
- { name: '西环路河道排放口', lng: 110.372, lat: 28.449, depth: 28.6, level: '严重积水' },
|
|
|
|
|
- { name: '东环路市政大楼段', lng: 110.410, lat: 28.454, depth: 0, level: '未积水' },
|
|
|
|
|
- { name: '开发区管委会路段', lng: 110.415, lat: 28.458, depth: 12.4, level: '中度积水' },
|
|
|
|
|
- { name: '城北二号排水井A7', lng: 110.394, lat: 28.465, depth: 0, level: '未积水' },
|
|
|
|
|
- { name: '白羊坪村监测点', lng: 110.380, lat: 28.468, depth: 0, level: '未积水' },
|
|
|
|
|
- { name: '杜家山监测站', lng: 110.405, lat: 28.467, depth: 2.1, level: '轻度积水' },
|
|
|
|
|
- { name: '阳光水岸监测点', lng: 110.388, lat: 28.445, depth: 0, level: '未积水' },
|
|
|
|
|
- { name: '大营村监测站', lng: 110.418, lat: 28.446, depth: 0, level: '未积水' },
|
|
|
|
|
- { name: '能瓦溪村委监测点', lng: 110.398, lat: 28.470, depth: 0, level: '未积水' }
|
|
|
|
|
-])
|
|
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ console.error('加载易积水点数据失败:', e)
|
|
|
|
|
+ ElMessage.error('加载易积水点数据失败')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
// ==================== 导航方法 ====================
|
|
// ==================== 导航方法 ====================
|
|
|
function goBack() {
|
|
function goBack() {
|
|
@@ -297,14 +371,24 @@ function initMap() {
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
mapInstance = new BMapGL.Map('jsqcMap')
|
|
mapInstance = new BMapGL.Map('jsqcMap')
|
|
|
- const centerPoint = new BMapGL.Point(110.393, 28.452)
|
|
|
|
|
- mapInstance.centerAndZoom(centerPoint, 15)
|
|
|
|
|
mapInstance.enableScrollWheelZoom(true)
|
|
mapInstance.enableScrollWheelZoom(true)
|
|
|
|
|
|
|
|
// 添加标记点
|
|
// 添加标记点
|
|
|
mapPoints.value.forEach(point => {
|
|
mapPoints.value.forEach(point => {
|
|
|
addMapMarker(point)
|
|
addMapMarker(point)
|
|
|
})
|
|
})
|
|
|
|
|
+
|
|
|
|
|
+ // 根据所有点位自适应视野
|
|
|
|
|
+ if (mapPoints.value.length > 0) {
|
|
|
|
|
+ const viewPoints = mapPoints.value.map(p => new BMapGL.Point(p.lng, p.lat))
|
|
|
|
|
+ mapInstance.setViewport(viewPoints)
|
|
|
|
|
+ // 确保缩放级别不低于14,避免地图显示太小
|
|
|
|
|
+ if (mapInstance.getZoom() < 14) {
|
|
|
|
|
+ mapInstance.setZoom(14)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ mapInstance.centerAndZoom(new BMapGL.Point(110.393, 28.452), 14)
|
|
|
|
|
+ }
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
console.error('百度地图初始化失败:', e)
|
|
console.error('百度地图初始化失败:', e)
|
|
|
}
|
|
}
|
|
@@ -337,26 +421,14 @@ function addMapMarker(point) {
|
|
|
})
|
|
})
|
|
|
mapInstance.addOverlay(label)
|
|
mapInstance.addOverlay(label)
|
|
|
|
|
|
|
|
- // 点击标记弹出信息窗口
|
|
|
|
|
|
|
+ // 点击标记打开积水深度曲线弹窗
|
|
|
label.addEventListener('click', function () {
|
|
label.addEventListener('click', function () {
|
|
|
- const infoContent = `<div style="color:#333;font-size:13px;padding:4px;">
|
|
|
|
|
- <div style="font-weight:bold;margin-bottom:6px;">${point.name}</div>
|
|
|
|
|
- <div>积水量:<b style="color:${color}">${point.depth} cm</b></div>
|
|
|
|
|
- <div>级别:<b style="color:${color}">${point.level}</b></div>
|
|
|
|
|
- <div>经度:${point.lng}</div>
|
|
|
|
|
- <div>纬度:${point.lat}</div>
|
|
|
|
|
- </div>`
|
|
|
|
|
- const infoWindow = new BMapGL.InfoWindow(infoContent, {
|
|
|
|
|
- width: 200,
|
|
|
|
|
- title: ''
|
|
|
|
|
- })
|
|
|
|
|
- mapInstance.openInfoWindow(infoWindow, bPoint)
|
|
|
|
|
|
|
+ openDepthModal(point)
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ==================== ECharts 图表 ====================
|
|
// ==================== ECharts 图表 ====================
|
|
|
let pieChart = null
|
|
let pieChart = null
|
|
|
-let barChart = null
|
|
|
|
|
let lineChart = null
|
|
let lineChart = null
|
|
|
|
|
|
|
|
function initPieChart() {
|
|
function initPieChart() {
|
|
@@ -390,43 +462,8 @@ function initPieChart() {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function initBarChart() {
|
|
|
|
|
- const dom = document.getElementById('jsqcBarChart')
|
|
|
|
|
- if (!dom) return
|
|
|
|
|
- barChart = echarts.init(dom)
|
|
|
|
|
- const months = ['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12']
|
|
|
|
|
- const values = [3, 5, 10, 6, 4, 8, 12, 7, 3, 2, 5, 6]
|
|
|
|
|
- barChart.setOption({
|
|
|
|
|
- tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
|
|
|
- grid: { left: 35, right: 10, top: 10, bottom: 25 },
|
|
|
|
|
- xAxis: {
|
|
|
|
|
- type: 'category',
|
|
|
|
|
- data: months,
|
|
|
|
|
- axisLabel: { color: '#7ec8e3', fontSize: 10 },
|
|
|
|
|
- axisLine: { lineStyle: { color: '#1a3a5c' } }
|
|
|
|
|
- },
|
|
|
|
|
- yAxis: {
|
|
|
|
|
- type: 'value',
|
|
|
|
|
- axisLabel: { color: '#7ec8e3', fontSize: 10 },
|
|
|
|
|
- splitLine: { lineStyle: { color: '#1a3a5c' } },
|
|
|
|
|
- axisLine: { show: false }
|
|
|
|
|
- },
|
|
|
|
|
- series: [{
|
|
|
|
|
- type: 'bar',
|
|
|
|
|
- data: values,
|
|
|
|
|
- barWidth: 12,
|
|
|
|
|
- itemStyle: {
|
|
|
|
|
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
- { offset: 0, color: '#409eff' },
|
|
|
|
|
- { offset: 1, color: '#1a3a5c' }
|
|
|
|
|
- ]),
|
|
|
|
|
- borderRadius: [2, 2, 0, 0]
|
|
|
|
|
- }
|
|
|
|
|
- }]
|
|
|
|
|
- })
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
function initLineChart() {
|
|
function initLineChart() {
|
|
|
|
|
+ // TODO: 对接24小时积水深度趋势API后替换mock数据
|
|
|
const dom = document.getElementById('jsqcLineChart')
|
|
const dom = document.getElementById('jsqcLineChart')
|
|
|
if (!dom) return
|
|
if (!dom) return
|
|
|
lineChart = echarts.init(dom)
|
|
lineChart = echarts.init(dom)
|
|
@@ -471,8 +508,105 @@ function initLineChart() {
|
|
|
})
|
|
})
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// ==================== 积水深度曲线弹窗 ====================
|
|
|
|
|
+const depthModalVisible = ref(false)
|
|
|
|
|
+const selectedPointName = ref('')
|
|
|
|
|
+const currentDepthTab = ref('24h')
|
|
|
|
|
+const depthTabs = [
|
|
|
|
|
+ { label: '24小时', value: '24h' },
|
|
|
|
|
+ { label: '7天', value: '7d' },
|
|
|
|
|
+ { label: '15天', value: '15d' },
|
|
|
|
|
+ { label: '30天', value: '30d' }
|
|
|
|
|
+]
|
|
|
|
|
+let depthChart = null
|
|
|
|
|
+
|
|
|
|
|
+function openDepthModal(point) {
|
|
|
|
|
+ selectedPointName.value = point.name
|
|
|
|
|
+ currentDepthTab.value = '24h'
|
|
|
|
|
+ depthModalVisible.value = true
|
|
|
|
|
+ nextTick(() => {
|
|
|
|
|
+ initDepthChart()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function closeDepthModal() {
|
|
|
|
|
+ depthModalVisible.value = false
|
|
|
|
|
+ if (depthChart) {
|
|
|
|
|
+ depthChart.dispose()
|
|
|
|
|
+ depthChart = null
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function switchDepthTab(tab) {
|
|
|
|
|
+ currentDepthTab.value = tab
|
|
|
|
|
+ initDepthChart()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function generateDepthData(tab) {
|
|
|
|
|
+ let labels = []
|
|
|
|
|
+ let values = []
|
|
|
|
|
+ const now = new Date()
|
|
|
|
|
+ const pad = n => String(n).padStart(2, '0')
|
|
|
|
|
+ if (tab === '24h') {
|
|
|
|
|
+ for (let i = 0; i < 24; i++) {
|
|
|
|
|
+ labels.push(`${pad(i)}:00`)
|
|
|
|
|
+ const base = i < 8 || i > 20 ? Math.random() * 3 : Math.random() * 15 + 5
|
|
|
|
|
+ values.push(Math.round(base * 10) / 10)
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const count = tab === '7d' ? 7 : tab === '15d' ? 15 : 30
|
|
|
|
|
+ for (let i = count - 1; i >= 0; i--) {
|
|
|
|
|
+ const d = new Date(now.getTime() - i * 24 * 60 * 60 * 1000)
|
|
|
|
|
+ labels.push(`${pad(d.getMonth() + 1)}-${pad(d.getDate())}`)
|
|
|
|
|
+ values.push(Math.round((Math.random() * 20 + Math.random() * 5) * 10) / 10)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return { labels, values }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+function initDepthChart() {
|
|
|
|
|
+ const dom = document.getElementById('jsqcDepthChart')
|
|
|
|
|
+ if (!dom) return
|
|
|
|
|
+ if (depthChart) depthChart.dispose()
|
|
|
|
|
+ depthChart = echarts.init(dom)
|
|
|
|
|
+ const { labels, values } = generateDepthData(currentDepthTab.value)
|
|
|
|
|
+ depthChart.setOption({
|
|
|
|
|
+ tooltip: { trigger: 'axis' },
|
|
|
|
|
+ grid: { left: 50, right: 20, top: 20, bottom: 30 },
|
|
|
|
|
+ xAxis: {
|
|
|
|
|
+ type: 'category',
|
|
|
|
|
+ data: labels,
|
|
|
|
|
+ axisLabel: { color: '#7ec8e3', fontSize: 10 },
|
|
|
|
|
+ axisLine: { lineStyle: { color: '#1a3a5c' } }
|
|
|
|
|
+ },
|
|
|
|
|
+ yAxis: {
|
|
|
|
|
+ type: 'value',
|
|
|
|
|
+ name: 'cm',
|
|
|
|
|
+ nameTextStyle: { color: '#7ec8e3', fontSize: 11 },
|
|
|
|
|
+ axisLabel: { color: '#7ec8e3', fontSize: 10 },
|
|
|
|
|
+ splitLine: { lineStyle: { color: '#1a3a5c' } },
|
|
|
|
|
+ axisLine: { show: false }
|
|
|
|
|
+ },
|
|
|
|
|
+ series: [{
|
|
|
|
|
+ type: 'line',
|
|
|
|
|
+ data: values,
|
|
|
|
|
+ smooth: true,
|
|
|
|
|
+ symbol: 'circle',
|
|
|
|
|
+ symbolSize: 6,
|
|
|
|
|
+ lineStyle: { color: '#409eff', width: 2 },
|
|
|
|
|
+ itemStyle: { color: '#409eff' },
|
|
|
|
|
+ areaStyle: {
|
|
|
|
|
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
|
|
|
|
+ { offset: 0, color: 'rgba(64, 158, 255, 0.4)' },
|
|
|
|
|
+ { offset: 1, color: 'rgba(64, 158, 255, 0.05)' }
|
|
|
|
|
+ ])
|
|
|
|
|
+ }
|
|
|
|
|
+ }]
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
// ==================== 生命周期 ====================
|
|
// ==================== 生命周期 ====================
|
|
|
-onMounted(() => {
|
|
|
|
|
|
|
+onMounted(async () => {
|
|
|
updateTime()
|
|
updateTime()
|
|
|
timer = setInterval(() => {
|
|
timer = setInterval(() => {
|
|
|
updateTime()
|
|
updateTime()
|
|
@@ -480,31 +614,33 @@ onMounted(() => {
|
|
|
}, 1000)
|
|
}, 1000)
|
|
|
updateRefreshTime()
|
|
updateRefreshTime()
|
|
|
|
|
|
|
|
|
|
+ // 先加载后端数据,再初始化地图和图表
|
|
|
|
|
+ await Promise.all([
|
|
|
|
|
+ loadWaterloggingData(),
|
|
|
|
|
+ loadWeatherData()
|
|
|
|
|
+ ])
|
|
|
|
|
+
|
|
|
// 延迟初始化确保 DOM 渲染完成
|
|
// 延迟初始化确保 DOM 渲染完成
|
|
|
setTimeout(() => {
|
|
setTimeout(() => {
|
|
|
initMap()
|
|
initMap()
|
|
|
initPieChart()
|
|
initPieChart()
|
|
|
- initBarChart()
|
|
|
|
|
initLineChart()
|
|
initLineChart()
|
|
|
}, 300)
|
|
}, 300)
|
|
|
-
|
|
|
|
|
- // 默认执行一次分析
|
|
|
|
|
- startAnalysis()
|
|
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
|
if (timer) clearInterval(timer)
|
|
if (timer) clearInterval(timer)
|
|
|
if (pieChart) pieChart.dispose()
|
|
if (pieChart) pieChart.dispose()
|
|
|
- if (barChart) barChart.dispose()
|
|
|
|
|
if (lineChart) lineChart.dispose()
|
|
if (lineChart) lineChart.dispose()
|
|
|
|
|
+ if (depthChart) depthChart.dispose()
|
|
|
if (mapInstance) mapInstance = null
|
|
if (mapInstance) mapInstance = null
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// 监听窗口大小变化,重绘图表
|
|
// 监听窗口大小变化,重绘图表
|
|
|
window.addEventListener('resize', () => {
|
|
window.addEventListener('resize', () => {
|
|
|
if (pieChart) pieChart.resize()
|
|
if (pieChart) pieChart.resize()
|
|
|
- if (barChart) barChart.resize()
|
|
|
|
|
if (lineChart) lineChart.resize()
|
|
if (lineChart) lineChart.resize()
|
|
|
|
|
+ if (depthChart) depthChart.resize()
|
|
|
})
|
|
})
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
@@ -704,35 +840,6 @@ window.addEventListener('resize', () => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* ==================== 监测点概况 ==================== */
|
|
/* ==================== 监测点概况 ==================== */
|
|
|
-.monitor-stats-row {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- margin-bottom: 8px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-tag {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-tag.online {
|
|
|
|
|
- color: #67c23a;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-tag.offline {
|
|
|
|
|
- color: #909399;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-tag.rate {
|
|
|
|
|
- color: #409eff;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.stat-divider {
|
|
|
|
|
- color: #1a3a5c;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
.pie-chart-wrapper {
|
|
.pie-chart-wrapper {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
display: flex;
|
|
display: flex;
|
|
@@ -858,37 +965,6 @@ window.addEventListener('resize', () => {
|
|
|
min-height: 0;
|
|
min-height: 0;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.map-search {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- top: 12px;
|
|
|
|
|
- left: 12px;
|
|
|
|
|
- z-index: 10;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.search-input {
|
|
|
|
|
- background: rgba(6, 30, 65, 0.85);
|
|
|
|
|
- border: 1px solid rgba(64, 158, 255, 0.3);
|
|
|
|
|
- color: #a0cfff;
|
|
|
|
|
- padding: 6px 30px 6px 12px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- outline: none;
|
|
|
|
|
- width: 160px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.search-input::placeholder {
|
|
|
|
|
- color: #4a6a8a;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.search-icon {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- right: 8px;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
.map-tabs {
|
|
.map-tabs {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
gap: 0;
|
|
gap: 0;
|
|
@@ -925,123 +1001,109 @@ window.addEventListener('resize', () => {
|
|
|
background: rgba(64, 158, 255, 0.15);
|
|
background: rgba(64, 158, 255, 0.15);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* ==================== 积水分析 ==================== */
|
|
|
|
|
-.analysis-filter {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 6px;
|
|
|
|
|
- margin-bottom: 8px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
|
|
+.chart-area-sm {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ flex: 1;
|
|
|
|
|
+ min-height: 130px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.dark-select {
|
|
|
|
|
- background: rgba(10, 25, 50, 0.8);
|
|
|
|
|
- border: 1px solid rgba(64, 158, 255, 0.3);
|
|
|
|
|
- color: #a0cfff;
|
|
|
|
|
- padding: 4px 8px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- outline: none;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
|
|
+/* ==================== 滚动条样式 ==================== */
|
|
|
|
|
+::-webkit-scrollbar {
|
|
|
|
|
+ width: 4px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.dark-select option {
|
|
|
|
|
- background: #0a1929;
|
|
|
|
|
- color: #a0cfff;
|
|
|
|
|
|
|
+::-webkit-scrollbar-track {
|
|
|
|
|
+ background: rgba(10, 25, 50, 0.3);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.analysis-btn {
|
|
|
|
|
- background: rgba(64, 158, 255, 0.2);
|
|
|
|
|
- border: 1px solid rgba(64, 158, 255, 0.5);
|
|
|
|
|
- color: #7ec8e3;
|
|
|
|
|
- padding: 4px 12px;
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- cursor: pointer;
|
|
|
|
|
- transition: all 0.3s;
|
|
|
|
|
|
|
+::-webkit-scrollbar-thumb {
|
|
|
|
|
+ background: rgba(64, 158, 255, 0.3);
|
|
|
|
|
+ border-radius: 2px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.analysis-btn:hover {
|
|
|
|
|
- background: rgba(64, 158, 255, 0.35);
|
|
|
|
|
- color: #fff;
|
|
|
|
|
|
|
+::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
+ background: rgba(64, 158, 255, 0.5);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.analysis-result {
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- color: #a0cfff;
|
|
|
|
|
- line-height: 1.8;
|
|
|
|
|
- margin-bottom: 8px;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- background: rgba(10, 25, 50, 0.5);
|
|
|
|
|
- border-radius: 4px;
|
|
|
|
|
- border-left: 3px solid #409eff;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
|
|
+/* ==================== 积水深度曲线弹窗 ==================== */
|
|
|
|
|
+.depth-modal {
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.7);
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ z-index: 1000;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.chart-area-sm {
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- height: 130px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
|
|
+.depth-modal-content {
|
|
|
|
|
+ width: 720px;
|
|
|
|
|
+ max-width: 90vw;
|
|
|
|
|
+ background: rgba(6, 30, 65, 0.95);
|
|
|
|
|
+ border: 1px solid rgba(64, 158, 255, 0.4);
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ box-shadow: 0 0 20px rgba(64, 158, 255, 0.2);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/* ==================== 预警信息 ==================== */
|
|
|
|
|
-.alert-stats {
|
|
|
|
|
|
|
+.depth-modal-header {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- gap: 8px;
|
|
|
|
|
- margin-bottom: 8px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.alert-stat-card {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- background: rgba(10, 25, 50, 0.6);
|
|
|
|
|
- border: 1px solid rgba(64, 158, 255, 0.15);
|
|
|
|
|
- border-radius: 6px;
|
|
|
|
|
- padding: 8px;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.alert-stat-val {
|
|
|
|
|
- font-size: 20px;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
- color: #e6a23c;
|
|
|
|
|
|
|
+.depth-modal-title {
|
|
|
|
|
+ font-size: 15px;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #7ec8e3;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.alert-stat-val.green {
|
|
|
|
|
- color: #67c23a;
|
|
|
|
|
|
|
+.depth-modal-close {
|
|
|
|
|
+ font-size: 22px;
|
|
|
|
|
+ color: #a0cfff;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ line-height: 1;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.alert-stat-val.red {
|
|
|
|
|
- color: #f56c6c;
|
|
|
|
|
|
|
+.depth-modal-close:hover {
|
|
|
|
|
+ color: #fff;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.alert-stat-label {
|
|
|
|
|
- font-size: 10px;
|
|
|
|
|
- color: #7ec8e3;
|
|
|
|
|
- margin-top: 2px;
|
|
|
|
|
|
|
+.depth-modal-tabs {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-bottom: 12px;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-.alert-table-wrap {
|
|
|
|
|
|
|
+.depth-modal-tab {
|
|
|
flex: 1;
|
|
flex: 1;
|
|
|
- overflow-y: auto;
|
|
|
|
|
- min-height: 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* ==================== 滚动条样式 ==================== */
|
|
|
|
|
-::-webkit-scrollbar {
|
|
|
|
|
- width: 4px;
|
|
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ padding: 6px 4px;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #a0cfff;
|
|
|
|
|
+ background: rgba(10, 25, 50, 0.6);
|
|
|
|
|
+ border: 1px solid rgba(64, 158, 255, 0.2);
|
|
|
|
|
+ border-radius: 4px;
|
|
|
|
|
+ cursor: pointer;
|
|
|
|
|
+ transition: all 0.3s;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-::-webkit-scrollbar-track {
|
|
|
|
|
- background: rgba(10, 25, 50, 0.3);
|
|
|
|
|
|
|
+.depth-modal-tab.active {
|
|
|
|
|
+ background: rgba(64, 158, 255, 0.3);
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ border-color: rgba(64, 158, 255, 0.6);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-::-webkit-scrollbar-thumb {
|
|
|
|
|
- background: rgba(64, 158, 255, 0.3);
|
|
|
|
|
- border-radius: 2px;
|
|
|
|
|
|
|
+.depth-modal-tab:hover {
|
|
|
|
|
+ background: rgba(64, 158, 255, 0.2);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-::-webkit-scrollbar-thumb:hover {
|
|
|
|
|
- background: rgba(64, 158, 255, 0.5);
|
|
|
|
|
|
|
+.depth-chart-area {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ height: 320px;
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|