|
|
@@ -0,0 +1,690 @@
|
|
|
+<template>
|
|
|
+ <div class="gis-page">
|
|
|
+ <div class="gis-header">
|
|
|
+ <div class="header-left">
|
|
|
+ <el-select v-model="selectedUnit" placeholder="请选择单位" @change="handleUnitChange" class="unit-select">
|
|
|
+ <el-option v-for="unit in unitList" :key="unit.unitCode" :label="unit.unitName" :value="unit.unitCode" />
|
|
|
+ </el-select>
|
|
|
+ </div>
|
|
|
+ <div class="header-center">
|
|
|
+ <div class="biz-buttons">
|
|
|
+ <el-button
|
|
|
+ v-for="biz in bizList"
|
|
|
+ :key="biz.bizCode"
|
|
|
+ :class="{ active: selectedBiz === biz.bizCode }"
|
|
|
+ @click="handleBizClick(biz.bizCode)"
|
|
|
+ size="default"
|
|
|
+ >
|
|
|
+ {{ biz.bizName }}
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="header-right">
|
|
|
+ <span class="station-count" v-if="selectedBiz">共 {{ stationList.length }} 个站点</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="gis-content">
|
|
|
+ <div ref="mapContainer" class="map-container"></div>
|
|
|
+
|
|
|
+ <div class="sidebar">
|
|
|
+ <div class="sidebar-header">
|
|
|
+ <span class="sidebar-title">站点列表</span>
|
|
|
+ <div class="search-box">
|
|
|
+ <el-input v-model="searchKeyword" placeholder="请输入站名" @keyup.enter="searchStation" clearable>
|
|
|
+ <template #prefix>
|
|
|
+ <el-icon><Search /></el-icon>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="station-list">
|
|
|
+ <div
|
|
|
+ v-for="station in filteredStations"
|
|
|
+ :key="station.id"
|
|
|
+ class="station-item"
|
|
|
+ :class="{ active: selectedStation?.id === station.id }"
|
|
|
+ @click="selectStation(station)"
|
|
|
+ >
|
|
|
+ <div class="station-icon">
|
|
|
+ <el-icon class="icon-map-marker">
|
|
|
+ <Location />
|
|
|
+ </el-icon>
|
|
|
+ </div>
|
|
|
+ <div class="station-info">
|
|
|
+ <div class="station-name">{{ station.stnm }}</div>
|
|
|
+ <div class="station-extra">{{ station.stcd }} · {{ station.rvnm || station.hnnm }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-if="filteredStations.length === 0" class="empty-tip">
|
|
|
+ <el-icon class="empty-icon"><MapLocation /></el-icon>
|
|
|
+ <p>暂无站点数据</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="sidebar-footer">
|
|
|
+ <span>共 {{ filteredStations.length }} 个站点</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-dialog
|
|
|
+ v-model="dialogVisible"
|
|
|
+ :title="selectedStation?.stnm + ' - 监测数据'"
|
|
|
+ width="600px"
|
|
|
+ @close="closeDialog"
|
|
|
+ >
|
|
|
+ <div v-if="selectedStation" class="station-detail">
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="section-title">站点信息</div>
|
|
|
+ <div class="info-grid">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">站码:</span>
|
|
|
+ <span class="value">{{ selectedStation.stcd }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">站名:</span>
|
|
|
+ <span class="value">{{ selectedStation.stnm }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">站址:</span>
|
|
|
+ <span class="value">{{ selectedStation.stlc || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">经度:</span>
|
|
|
+ <span class="value">{{ selectedStation.lgtd }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">纬度:</span>
|
|
|
+ <span class="value">{{ selectedStation.lttd }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="label">所属业务:</span>
|
|
|
+ <span class="value">{{ selectedStation.bizName }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="detail-section">
|
|
|
+ <div class="section-title">监测数据</div>
|
|
|
+ <div v-if="qualityData.length > 0" class="quality-table">
|
|
|
+ <table>
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>分析日期</th>
|
|
|
+ <th>评估类型</th>
|
|
|
+ <th>评估值</th>
|
|
|
+ <th>数据条数</th>
|
|
|
+ <th>描述</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="item in qualityData" :key="item.id">
|
|
|
+ <td>{{ formatDate(item.analysisDate) }}</td>
|
|
|
+ <td>{{ item.type || '-' }}</td>
|
|
|
+ <td>{{ item.value || '-' }}</td>
|
|
|
+ <td>{{ item.cnt || 0 }}</td>
|
|
|
+ <td>{{ item.description || '-' }}</td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ <div v-else class="no-data">
|
|
|
+ <el-icon class="empty-icon"><DataAnalysis /></el-icon>
|
|
|
+ <p>暂无监测数据</p>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { ref, onMounted, onUnmounted, computed, watch, nextTick } from 'vue'
|
|
|
+import { Map as OlMap, View, Overlay } from 'ol'
|
|
|
+import { OSM, Vector as VectorSource } from 'ol/source'
|
|
|
+import TileLayer from 'ol/layer/Tile'
|
|
|
+import VectorLayer from 'ol/layer/Vector'
|
|
|
+import { fromLonLat, toLonLat } from 'ol/proj'
|
|
|
+import { Feature } from 'ol'
|
|
|
+import { Point } from 'ol/geom'
|
|
|
+import { Icon, Style } from 'ol/style'
|
|
|
+import { Search, MapLocation, Location, DataAnalysis } from '@element-plus/icons-vue'
|
|
|
+import request from '@/utils/request'
|
|
|
+import 'ol/ol.css'
|
|
|
+
|
|
|
+const mapContainer = ref(null)
|
|
|
+const selectedUnit = ref('')
|
|
|
+const selectedBiz = ref('')
|
|
|
+const searchKeyword = ref('')
|
|
|
+const dialogVisible = ref(false)
|
|
|
+const selectedStation = ref(null)
|
|
|
+const qualityData = ref([])
|
|
|
+
|
|
|
+const unitList = ref([])
|
|
|
+const bizList = ref([])
|
|
|
+const stationList = ref([])
|
|
|
+
|
|
|
+let olMap = null
|
|
|
+let markerLayer = null
|
|
|
+
|
|
|
+const filteredStations = computed(() => {
|
|
|
+ let stations = stationList.value
|
|
|
+ if (searchKeyword.value) {
|
|
|
+ const keyword = searchKeyword.value.toLowerCase()
|
|
|
+ stations = stations.filter(s =>
|
|
|
+ s.stnm.toLowerCase().includes(keyword) ||
|
|
|
+ s.stcd.toLowerCase().includes(keyword)
|
|
|
+ )
|
|
|
+ }
|
|
|
+ return stations
|
|
|
+})
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ loadUnits()
|
|
|
+ setTimeout(() => {
|
|
|
+ if (mapContainer.value) {
|
|
|
+ initMap()
|
|
|
+ } else {
|
|
|
+ console.error('Map container not found')
|
|
|
+ }
|
|
|
+ }, 100)
|
|
|
+})
|
|
|
+
|
|
|
+onUnmounted(() => {
|
|
|
+ if (olMap) {
|
|
|
+ olMap.dispose()
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
+function loadUnits() {
|
|
|
+ request({
|
|
|
+ url: '/gx/unit/front/list',
|
|
|
+ method: 'get'
|
|
|
+ }).then(res => {
|
|
|
+ unitList.value = res.data || []
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function handleUnitChange(val) {
|
|
|
+ selectedBiz.value = ''
|
|
|
+ stationList.value = []
|
|
|
+ clearMarkers()
|
|
|
+
|
|
|
+ if (val) {
|
|
|
+ request({
|
|
|
+ url: '/gx/biz/front/list',
|
|
|
+ method: 'get',
|
|
|
+ params: { bizUnit: val }
|
|
|
+ }).then(res => {
|
|
|
+ bizList.value = res.data || []
|
|
|
+ if (bizList.value.length > 0) {
|
|
|
+ selectedBiz.value = bizList.value[0].bizCode
|
|
|
+ handleBizClick(bizList.value[0].bizCode)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ } else {
|
|
|
+ bizList.value = []
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function handleBizClick(val) {
|
|
|
+ selectedBiz.value = val
|
|
|
+ stationList.value = []
|
|
|
+ clearMarkers()
|
|
|
+
|
|
|
+ if (val) {
|
|
|
+ request({
|
|
|
+ url: '/gx/station/front/listByBizCode/' + val,
|
|
|
+ method: 'get'
|
|
|
+ }).then(res => {
|
|
|
+ stationList.value = res.data || res.rows || []
|
|
|
+ addMarkers()
|
|
|
+
|
|
|
+ if (stationList.value.length > 0) {
|
|
|
+ const firstStation = stationList.value[0]
|
|
|
+ if (firstStation.lgtd && firstStation.lttd) {
|
|
|
+ const coord = fromLonLat([Number(firstStation.lgtd), Number(firstStation.lttd)])
|
|
|
+ olMap.getView().animate({
|
|
|
+ center: coord,
|
|
|
+ zoom: 10,
|
|
|
+ duration: 1000
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function searchStation() {
|
|
|
+}
|
|
|
+
|
|
|
+function selectStation(station) {
|
|
|
+ selectedStation.value = station
|
|
|
+
|
|
|
+ if (station.lgtd && station.lttd) {
|
|
|
+ const coord = fromLonLat([Number(station.lgtd), Number(station.lttd)])
|
|
|
+ olMap.getView().animate({
|
|
|
+ center: coord,
|
|
|
+ zoom: 12,
|
|
|
+ duration: 800
|
|
|
+ })
|
|
|
+
|
|
|
+ highlightMarker(station)
|
|
|
+ }
|
|
|
+
|
|
|
+ loadQualityData(station.stcd)
|
|
|
+ dialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+function loadQualityData(stcd) {
|
|
|
+ request({
|
|
|
+ url: '/gx/quality/front/list',
|
|
|
+ method: 'get',
|
|
|
+ params: { stcd: stcd }
|
|
|
+ }).then(res => {
|
|
|
+ qualityData.value = res.data || []
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function closeDialog() {
|
|
|
+ dialogVisible.value = false
|
|
|
+ selectedStation.value = null
|
|
|
+ qualityData.value = []
|
|
|
+}
|
|
|
+
|
|
|
+function initMap() {
|
|
|
+ const tdtUrl = 'http://t0.tianditu.gov.cn/DataServer?T=vec_w&X={x}&Y={y}&L={z}&tk=5c5a468d333dbb827f134fb64818aa65'
|
|
|
+
|
|
|
+ olMap = new OlMap({
|
|
|
+ target: mapContainer.value,
|
|
|
+ view: new View({
|
|
|
+ center: fromLonLat([120.5, 31.5]),
|
|
|
+ zoom: 9,
|
|
|
+ minZoom: 6,
|
|
|
+ maxZoom: 18
|
|
|
+ }),
|
|
|
+ layers: [
|
|
|
+ new TileLayer({
|
|
|
+ source: new OSM({
|
|
|
+ url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ ]
|
|
|
+ })
|
|
|
+
|
|
|
+ markerLayer = new VectorLayer({
|
|
|
+ source: new VectorSource()
|
|
|
+ })
|
|
|
+ olMap.addLayer(markerLayer)
|
|
|
+
|
|
|
+ olMap.on('click', handleMapClick)
|
|
|
+}
|
|
|
+
|
|
|
+function addMarkers() {
|
|
|
+ const source = markerLayer.getSource()
|
|
|
+ source.clear()
|
|
|
+
|
|
|
+ stationList.value.forEach(station => {
|
|
|
+ if (station.lgtd && station.lttd) {
|
|
|
+ const coord = fromLonLat([Number(station.lgtd), Number(station.lttd)])
|
|
|
+ const feature = new Feature({
|
|
|
+ geometry: new Point(coord),
|
|
|
+ station: station
|
|
|
+ })
|
|
|
+
|
|
|
+ feature.setStyle(new Style({
|
|
|
+ image: new Icon({
|
|
|
+ src: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSI4IiBmaWxsPSIjMDA2NmNjIiBmaWxsLW9wYWNpdHk9IjAuOCIvPjxjaXJjbGUgY3g9IjEwIiBjeT0iMTAiIHI9IjQiIGZpbGw9IiNmZmQiIGZpbGwtb3BhY2l0eT0iMC45Ii8+PC9zdmc+',
|
|
|
+ anchor: [0.5, 1],
|
|
|
+ scale: 1.2
|
|
|
+ })
|
|
|
+ }))
|
|
|
+
|
|
|
+ source.addFeature(feature)
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function clearMarkers() {
|
|
|
+ if (markerLayer) {
|
|
|
+ markerLayer.getSource().clear()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function highlightMarker(station) {
|
|
|
+ const source = markerLayer.getSource()
|
|
|
+ const features = source.getFeatures()
|
|
|
+
|
|
|
+ features.forEach(feature => {
|
|
|
+ const s = feature.get('station')
|
|
|
+ const isSelected = s && s.id === station.id
|
|
|
+
|
|
|
+ feature.setStyle(new Style({
|
|
|
+ image: new Icon({
|
|
|
+ src: isSelected
|
|
|
+ ? 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSI4IiBmaWxsPSIjZmY2MDAwIiBmaWxsLW9wYWNpdHk9IjAuOCIvPjxjaXJjbGUgY3g9IjEwIiBjeT0iMTAiIHI9IjQiIGZpbGw9IiNmZmQiIGZpbGwtb3BhY2l0eT0iMC45Ii8+PC9zdmc+'
|
|
|
+ : 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAiIGhlaWdodD0iMjAiIHZpZXdCb3g9IjAgMCAyMCAyMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48Y2lyY2xlIGN4PSIxMCIgY3k9IjEwIiByPSI4IiBmaWxsPSIjMDA2NmNjIiBmaWxsLW9wYWNpdHk9IjAuOCIvPjxjaXJjbGUgY3g9IjEwIiBjeT0iMTAiIHI9IjQiIGZpbGw9IiNmZmQiIGZpbGwtb3BhY2l0eT0iMC45Ii8+PC9zdmc+',
|
|
|
+ anchor: [0.5, 1],
|
|
|
+ scale: isSelected ? 1.5 : 1.2
|
|
|
+ })
|
|
|
+ }))
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+function handleMapClick(event) {
|
|
|
+ const feature = olMap.forEachFeatureAtPixel(event.pixel, (feature) => feature)
|
|
|
+
|
|
|
+ if (feature) {
|
|
|
+ const station = feature.get('station')
|
|
|
+ if (station) {
|
|
|
+ selectStation(station)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function formatDate(dateStr) {
|
|
|
+ if (!dateStr) return '-'
|
|
|
+ const date = new Date(dateStr)
|
|
|
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.gis-page {
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ background: #f5f7fa;
|
|
|
+}
|
|
|
+
|
|
|
+.gis-header {
|
|
|
+ padding: 15px 20px;
|
|
|
+ background: #fff;
|
|
|
+ border-bottom: 1px solid #e4e7ed;
|
|
|
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+}
|
|
|
+
|
|
|
+.header-left {
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.unit-select {
|
|
|
+ width: 220px;
|
|
|
+}
|
|
|
+
|
|
|
+.header-center {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 0 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.biz-buttons {
|
|
|
+ display: flex;
|
|
|
+ gap: 10px;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+
|
|
|
+.biz-buttons :deep(.el-button) {
|
|
|
+ padding: 6px 20px;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 14px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ color: #606266;
|
|
|
+ border-color: #dcdfe6;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+}
|
|
|
+
|
|
|
+.biz-buttons :deep(.el-button:hover) {
|
|
|
+ background: #ecf5ff;
|
|
|
+ border-color: #b3d8ff;
|
|
|
+ color: #1c97e7;
|
|
|
+}
|
|
|
+
|
|
|
+.biz-buttons :deep(.el-button.active) {
|
|
|
+ background: #1c97e7;
|
|
|
+ color: #fff;
|
|
|
+ border-color: #1c97e7;
|
|
|
+}
|
|
|
+
|
|
|
+.header-right {
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.station-count {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #909399;
|
|
|
+ padding: 0 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.gis-content {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.map-container {
|
|
|
+ flex: 1;
|
|
|
+ height: calc(100% - 70px);
|
|
|
+ position: relative;
|
|
|
+ min-height: 400px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar {
|
|
|
+ width: 320px;
|
|
|
+ background: #fff;
|
|
|
+ border-left: 1px solid #e4e7ed;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ box-shadow: -2px 0 10px rgba(0, 0, 0, 0.05);
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-header {
|
|
|
+ padding: 15px;
|
|
|
+ border-bottom: 1px solid #e4e7ed;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-title {
|
|
|
+ font-size: 16px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.search-box {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+
|
|
|
+.station-list {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.station-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 12px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ background: #fafafa;
|
|
|
+ border-radius: 8px;
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.3s ease;
|
|
|
+ border: 2px solid transparent;
|
|
|
+}
|
|
|
+
|
|
|
+.station-item:hover {
|
|
|
+ background: #ecf5ff;
|
|
|
+ border-color: #b3d8ff;
|
|
|
+}
|
|
|
+
|
|
|
+.station-item.active {
|
|
|
+ background: #e6f7ff;
|
|
|
+ border-color: #1c97e7;
|
|
|
+}
|
|
|
+
|
|
|
+.station-icon {
|
|
|
+ width: 40px;
|
|
|
+ height: 40px;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #1c97e7;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ margin-right: 12px;
|
|
|
+ flex-shrink: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.station-icon .icon-map-marker {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.station-info {
|
|
|
+ flex: 1;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+.station-name {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.station-extra {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-tip {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 60px 20px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-icon {
|
|
|
+ font-size: 48px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ opacity: 0.5;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-tip p {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+.sidebar-footer {
|
|
|
+ padding: 12px 15px;
|
|
|
+ border-top: 1px solid #e4e7ed;
|
|
|
+ text-align: center;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.station-detail {
|
|
|
+ padding: 10px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.detail-section {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.section-title {
|
|
|
+ font-size: 15px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #303133;
|
|
|
+ padding-bottom: 10px;
|
|
|
+ border-bottom: 2px solid #1c97e7;
|
|
|
+ margin-bottom: 15px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
+ gap: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item .label {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #909399;
|
|
|
+ margin-bottom: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+.info-item .value {
|
|
|
+ font-size: 14px;
|
|
|
+ color: #303133;
|
|
|
+ font-weight: 500;
|
|
|
+}
|
|
|
+
|
|
|
+.quality-table {
|
|
|
+ max-height: 300px;
|
|
|
+ overflow-y: auto;
|
|
|
+}
|
|
|
+
|
|
|
+.quality-table table {
|
|
|
+ width: 100%;
|
|
|
+ border-collapse: collapse;
|
|
|
+}
|
|
|
+
|
|
|
+.quality-table th,
|
|
|
+.quality-table td {
|
|
|
+ padding: 10px;
|
|
|
+ text-align: left;
|
|
|
+ border-bottom: 1px solid #ebf0f5;
|
|
|
+ font-size: 13px;
|
|
|
+}
|
|
|
+
|
|
|
+.quality-table th {
|
|
|
+ background: #fafafa;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #606266;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.quality-table td {
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.no-data {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ padding: 40px 20px;
|
|
|
+ color: #909399;
|
|
|
+}
|
|
|
+
|
|
|
+.no-data .empty-icon {
|
|
|
+ font-size: 40px;
|
|
|
+ margin-bottom: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.no-data p {
|
|
|
+ margin: 0;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+</style>
|