|
@@ -1,17 +1,9 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="gis-dashboard">
|
|
|
|
|
- <div class="dashboard-header">
|
|
|
|
|
- <div class="header-title">
|
|
|
|
|
- <div class="title-main">
|
|
|
|
|
- <el-icon><MapLocation /></el-icon>
|
|
|
|
|
- <span>供水设施GIS一张图</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div class="title-metrics">
|
|
|
|
|
- <span class="metric metric-source">水源地 {{ summaryStats.sourceCount }}处</span>
|
|
|
|
|
- <span class="metric metric-plant">水厂 {{ summaryStats.plantCount }}座</span>
|
|
|
|
|
- <span class="metric metric-pumpStation">泵站 {{ summaryStats.pumpStationCount }}座</span>
|
|
|
|
|
- <span class="metric metric-pipe">供水管网 {{ summaryStats.pipeCount }}条</span>
|
|
|
|
|
- </div>
|
|
|
|
|
|
|
+ <div class="water-device-gis facility-gis">
|
|
|
|
|
+ <div class="gis-header">
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <p class="eyebrow">WATER SUPPLY FACILITY GIS</p>
|
|
|
|
|
+ <h2>供水设施 GIS 一张图</h2>
|
|
|
</div>
|
|
</div>
|
|
|
<div class="header-actions">
|
|
<div class="header-actions">
|
|
|
<el-input
|
|
<el-input
|
|
@@ -19,81 +11,73 @@
|
|
|
class="search-input"
|
|
class="search-input"
|
|
|
placeholder="搜索设施名称/编号/地址"
|
|
placeholder="搜索设施名称/编号/地址"
|
|
|
clearable
|
|
clearable
|
|
|
|
|
+ :prefix-icon="Search"
|
|
|
@keyup.enter="handleSearch"
|
|
@keyup.enter="handleSearch"
|
|
|
- >
|
|
|
|
|
- <template #prefix>
|
|
|
|
|
- <el-icon><Search /></el-icon>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-input>
|
|
|
|
|
- <el-button type="primary" @click="handleSearch">
|
|
|
|
|
- <el-icon><Search /></el-icon>
|
|
|
|
|
- 搜索
|
|
|
|
|
- </el-button>
|
|
|
|
|
- <el-button @click="refreshScene">
|
|
|
|
|
- <el-icon><RefreshRight /></el-icon>
|
|
|
|
|
- 刷新
|
|
|
|
|
- </el-button>
|
|
|
|
|
- <el-button @click="toggleFullscreen">
|
|
|
|
|
- <el-icon><FullScreen /></el-icon>
|
|
|
|
|
- 全图
|
|
|
|
|
- </el-button>
|
|
|
|
|
|
|
+ />
|
|
|
|
|
+ <el-select v-model="selectedFacilityType" class="type-select" placeholder="全部设施类型" clearable>
|
|
|
|
|
+ <el-option v-for="item in facilityTypes" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ <el-button type="primary" :icon="Refresh" @click="refreshScene">刷新</el-button>
|
|
|
|
|
+ <el-button :icon="Aim" @click="fitBounds">全图</el-button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <div ref="screenRef" class="dashboard-content">
|
|
|
|
|
- <aside class="dashboard-sidebar">
|
|
|
|
|
- <el-card shadow="never" class="panel-card">
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <span>图层控制</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- <div class="layer-group">
|
|
|
|
|
- <div class="layer-title">供水设施图层</div>
|
|
|
|
|
- <el-checkbox v-for="item in facilityTypes" :key="item.value" v-model="layerState[item.value]">{{ item.label }}</el-checkbox>
|
|
|
|
|
|
|
+ <div class="gis-layout">
|
|
|
|
|
+ <aside class="side-panel">
|
|
|
|
|
+ <div class="metric-grid">
|
|
|
|
|
+ <div class="metric">
|
|
|
|
|
+ <span>设施总数</span>
|
|
|
|
|
+ <strong>{{ summaryStats.total }}</strong>
|
|
|
</div>
|
|
</div>
|
|
|
- </el-card>
|
|
|
|
|
-
|
|
|
|
|
- <el-card shadow="never" class="panel-card">
|
|
|
|
|
- <template #header>
|
|
|
|
|
- <span>图例说明</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- <div class="legend-list">
|
|
|
|
|
- <div class="legend-item" v-for="item in facilityTypes" :key="item.value"><span class="legend-dot" :style="{ background: item.color }"></span><span>{{ item.label }}</span></div>
|
|
|
|
|
|
|
+ <div class="metric">
|
|
|
|
|
+ <span>供水管网</span>
|
|
|
|
|
+ <strong>{{ summaryStats.pipeCount }}</strong>
|
|
|
</div>
|
|
</div>
|
|
|
- </el-card>
|
|
|
|
|
-
|
|
|
|
|
- <el-card shadow="never" class="panel-card stats-card">
|
|
|
|
|
- <template #header>
|
|
|
|
|
-<span>供水设施统计</span>
|
|
|
|
|
- </template>
|
|
|
|
|
- <div class="stats-list">
|
|
|
|
|
- <div class="stats-row"><span>水源地</span><strong>{{ summaryStats.sourceCount }}</strong></div>
|
|
|
|
|
- <div class="stats-row"><span>水厂</span><strong>{{ summaryStats.plantCount }}</strong></div>
|
|
|
|
|
- <div class="stats-row"><span>泵站</span><strong>{{ summaryStats.pumpStationCount }}</strong></div>
|
|
|
|
|
- <div class="stats-row"><span>供水管网</span><strong>{{ summaryStats.pipeCount }}</strong></div>
|
|
|
|
|
- <div class="stats-row"><span>用水户</span><strong>{{ summaryStats.userCount }}</strong></div>
|
|
|
|
|
|
|
+ <div class="metric">
|
|
|
|
|
+ <span>水厂</span>
|
|
|
|
|
+ <strong>{{ summaryStats.plantCount }}</strong>
|
|
|
</div>
|
|
</div>
|
|
|
- </el-card>
|
|
|
|
|
|
|
+ <div class="metric">
|
|
|
|
|
+ <span>泵站</span>
|
|
|
|
|
+ <strong>{{ summaryStats.pumpStationCount }}</strong>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- </aside>
|
|
|
|
|
|
|
+ <div class="panel-section">
|
|
|
|
|
+ <h3>图层控制</h3>
|
|
|
|
|
+ <div class="layer-list">
|
|
|
|
|
+ <label v-for="item in facilityTypes" :key="item.value" class="layer-row">
|
|
|
|
|
+ <el-checkbox v-model="layerState[item.value]">{{ item.label }}</el-checkbox>
|
|
|
|
|
+ </label>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div class="panel-section legend-panel">
|
|
|
|
|
+ <h3>图例</h3>
|
|
|
|
|
+ <div class="legend-row" v-for="item in facilityTypes" :key="item.value">
|
|
|
|
|
+ <i class="legend-dot" :style="{ background: item.color }"></i>{{ item.label }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <section class="dashboard-map-panel">
|
|
|
|
|
- <div class="map-toolbar">
|
|
|
|
|
- <div class="toolbar-left">
|
|
|
|
|
- <el-button @click="zoomIn">
|
|
|
|
|
- <el-icon><Plus /></el-icon>
|
|
|
|
|
- 放大
|
|
|
|
|
- </el-button>
|
|
|
|
|
- <el-button @click="zoomOut">
|
|
|
|
|
- <el-icon><Minus /></el-icon>
|
|
|
|
|
- 缩小
|
|
|
|
|
- </el-button>
|
|
|
|
|
- <el-button @click="locateCenter">
|
|
|
|
|
- <el-icon><Location /></el-icon>
|
|
|
|
|
- 定位
|
|
|
|
|
- </el-button>
|
|
|
|
|
|
|
+ <div class="panel-section result-section">
|
|
|
|
|
+ <h3>搜索设施</h3>
|
|
|
|
|
+ <el-alert v-if="loadError" type="error" :closable="false" show-icon :title="loadError" />
|
|
|
|
|
+ <el-empty
|
|
|
|
|
+ v-else-if="!filteredFeatures.length"
|
|
|
|
|
+ description="暂无可显示的供水设施"
|
|
|
|
|
+ :image-size="80"
|
|
|
|
|
+ />
|
|
|
|
|
+ <div v-else class="device-result-list">
|
|
|
|
|
+ <button v-for="feature in filteredFeatures" :key="feature.id || feature.name" type="button" @click="locateFeature(feature)">
|
|
|
|
|
+ <span class="device-name">{{ feature.name }}</span>
|
|
|
|
|
+ <span class="device-meta">{{ labelMap[feature.type] || '供水设施' }} · {{ feature.code || '-' }}</span>
|
|
|
|
|
+ <el-tag size="small" type="primary">{{ labelMap[feature.type] || '供水设施' }}</el-tag>
|
|
|
|
|
+ </button>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="toolbar-right">当前坐标: {{ currentCoordinate }}</div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ </aside>
|
|
|
|
|
+
|
|
|
|
|
+ <main class="map-panel" v-loading="loading">
|
|
|
<GisMapCanvas
|
|
<GisMapCanvas
|
|
|
ref="gisMapRef"
|
|
ref="gisMapRef"
|
|
|
:points="mapPoints"
|
|
:points="mapPoints"
|
|
@@ -103,44 +87,49 @@
|
|
|
@point-click="openFacilityDetail"
|
|
@point-click="openFacilityDetail"
|
|
|
@line-click="openFacilityDetail"
|
|
@line-click="openFacilityDetail"
|
|
|
@map-click="detailOpen = false"
|
|
@map-click="detailOpen = false"
|
|
|
|
|
+ @ready="handleMapReady"
|
|
|
@error="mapError = $event"
|
|
@error="mapError = $event"
|
|
|
/>
|
|
/>
|
|
|
- <div v-if="mapError" class="map-error">
|
|
|
|
|
- <el-result icon="warning" title="供水设施图层加载失败" :sub-title="mapError">
|
|
|
|
|
- <template #extra><el-button type="primary" @click="loadGisData">重试</el-button></template>
|
|
|
|
|
- </el-result>
|
|
|
|
|
- </div>
|
|
|
|
|
- <div v-else-if="mapEmpty" class="map-error">
|
|
|
|
|
- <el-empty description="当前筛选下没有可显示的供水设施" />
|
|
|
|
|
- </div>
|
|
|
|
|
- </section>
|
|
|
|
|
|
|
+ <el-alert v-if="mapError" class="map-error" type="warning" show-icon :closable="false" :title="mapError" />
|
|
|
|
|
+ <el-alert v-else-if="loadError" class="map-error" type="error" show-icon :closable="false" :title="loadError" />
|
|
|
|
|
+ <el-empty
|
|
|
|
|
+ v-if="dataLoaded && !mapError && !loadError && !visibleFeatures.length"
|
|
|
|
|
+ class="map-empty"
|
|
|
|
|
+ description="当前筛选下没有可显示的供水设施"
|
|
|
|
|
+ />
|
|
|
|
|
+ </main>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
- <el-drawer
|
|
|
|
|
- v-model="detailOpen"
|
|
|
|
|
- :title="detailTitle"
|
|
|
|
|
- direction="rtl"
|
|
|
|
|
- class="detail-drawer"
|
|
|
|
|
- size="560px"
|
|
|
|
|
- append-to-body
|
|
|
|
|
- >
|
|
|
|
|
- <el-skeleton v-if="detailLoading" :rows="8" animated />
|
|
|
|
|
- <el-result v-else-if="detailError" icon="warning" title="设施详情加载失败" :sub-title="detailError">
|
|
|
|
|
- <template #extra><el-button type="primary" @click="loadFacilityDetail">重试</el-button></template>
|
|
|
|
|
- </el-result>
|
|
|
|
|
- <el-descriptions v-else-if="detailRecord" :column="1" border>
|
|
|
|
|
- <el-descriptions-item v-for="item in detailEntries" :key="item.label" :label="item.label">
|
|
|
|
|
- {{ item.value }}
|
|
|
|
|
- </el-descriptions-item>
|
|
|
|
|
- </el-descriptions>
|
|
|
|
|
- <el-empty v-else description="暂无设施详情" />
|
|
|
|
|
|
|
+ <el-drawer v-model="detailOpen" title="设施详情" size="420px" direction="rtl" class="detail-drawer" append-to-body>
|
|
|
|
|
+ <div v-loading="detailLoading" class="device-detail">
|
|
|
|
|
+ <el-alert v-if="detailError" type="error" show-icon :closable="false" title="设施详情加载失败,请重试" />
|
|
|
|
|
+ <template v-if="detailRecord">
|
|
|
|
|
+ <section class="detail-hero">
|
|
|
|
|
+ <h3>{{ detailTarget?.name || '供水设施' }}</h3>
|
|
|
|
|
+ <p>{{ labelMap[detailTarget?.type] || '供水设施' }} · {{ detailTarget?.code || '-' }}</p>
|
|
|
|
|
+ <div class="status-tags">
|
|
|
|
|
+ <el-tag type="primary">{{ labelMap[detailTarget?.type] || '供水设施' }}</el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </section>
|
|
|
|
|
+ <section class="detail-section">
|
|
|
|
|
+ <h4>基础信息</h4>
|
|
|
|
|
+ <dl>
|
|
|
|
|
+ <template v-for="item in detailEntries" :key="item.label">
|
|
|
|
|
+ <dt>{{ item.label }}</dt>
|
|
|
|
|
+ <dd>{{ item.value }}</dd>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </dl>
|
|
|
|
|
+ </section>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <el-empty v-else-if="!detailLoading && !detailError" description="暂无设施详情" />
|
|
|
|
|
+ </div>
|
|
|
</el-drawer>
|
|
</el-drawer>
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
|
|
|
|
|
-import { FullScreen, Location, MapLocation, RefreshRight, Search } from '@element-plus/icons-vue'
|
|
|
|
|
|
|
+import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
|
|
|
|
|
+import { Aim, Refresh, Search } from '@element-plus/icons-vue'
|
|
|
import { ElMessage } from 'element-plus'
|
|
import { ElMessage } from 'element-plus'
|
|
|
import GisMapCanvas from '@/components/GisMapCanvas.vue'
|
|
import GisMapCanvas from '@/components/GisMapCanvas.vue'
|
|
|
import { getWaterFacilityOverview, getWaterFacilityFeatures, getWaterFacilityDetail } from '@/api/pipeNetwork/waterSupply'
|
|
import { getWaterFacilityOverview, getWaterFacilityFeatures, getWaterFacilityDetail } from '@/api/pipeNetwork/waterSupply'
|
|
@@ -148,7 +137,7 @@ import { normalizeGeoJson, unwrapResponse } from '@/utils/waterSupplyModel'
|
|
|
import { getRequestErrorMessage } from '@/utils/waterFacilityQuery'
|
|
import { getRequestErrorMessage } from '@/utils/waterFacilityQuery'
|
|
|
import { buildFacilityMapModel, filterFacilityMapModel, findFacilityFeature } from '@/utils/waterFacilityGisModel'
|
|
import { buildFacilityMapModel, filterFacilityMapModel, findFacilityFeature } from '@/utils/waterFacilityGisModel'
|
|
|
|
|
|
|
|
-const centerPoint = { lng: 110.386, lat: 28.445 }
|
|
|
|
|
|
|
+const centerPoint = { lng: 110.3965, lat: 28.4638 }
|
|
|
|
|
|
|
|
const defaultLayerState = () => ({
|
|
const defaultLayerState = () => ({
|
|
|
source: true,
|
|
source: true,
|
|
@@ -182,12 +171,14 @@ const labelMap = {
|
|
|
user: '用水户'
|
|
user: '用水户'
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const screenRef = ref(null)
|
|
|
|
|
const gisMapRef = ref(null)
|
|
const gisMapRef = ref(null)
|
|
|
const searchKeyword = ref('')
|
|
const searchKeyword = ref('')
|
|
|
-const currentCoordinate = ref(`${centerPoint.lng}, ${centerPoint.lat}`)
|
|
|
|
|
|
|
+const selectedFacilityType = ref('')
|
|
|
const mapError = ref('')
|
|
const mapError = ref('')
|
|
|
|
|
+const loadError = ref('')
|
|
|
const dataLoaded = ref(false)
|
|
const dataLoaded = ref(false)
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const mapReady = ref(false)
|
|
|
const layerState = reactive(defaultLayerState())
|
|
const layerState = reactive(defaultLayerState())
|
|
|
const facilityOverview = ref([])
|
|
const facilityOverview = ref([])
|
|
|
const pipelineData = ref([])
|
|
const pipelineData = ref([])
|
|
@@ -204,6 +195,7 @@ const typeCount = type => {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const summaryStats = computed(() => ({
|
|
const summaryStats = computed(() => ({
|
|
|
|
|
+ total: facilityTypes.reduce((sum, item) => sum + typeCount(item.value), 0),
|
|
|
sourceCount: typeCount('source'),
|
|
sourceCount: typeCount('source'),
|
|
|
plantCount: typeCount('plant'),
|
|
plantCount: typeCount('plant'),
|
|
|
pumpStationCount: typeCount('pumpStation'),
|
|
pumpStationCount: typeCount('pumpStation'),
|
|
@@ -213,11 +205,20 @@ const summaryStats = computed(() => ({
|
|
|
|
|
|
|
|
const fullMapModel = computed(() => ({ points: pointData.value, lines: pipelineData.value }))
|
|
const fullMapModel = computed(() => ({ points: pointData.value, lines: pipelineData.value }))
|
|
|
const visibleMapModel = computed(() => filterFacilityMapModel(fullMapModel.value, layerState))
|
|
const visibleMapModel = computed(() => filterFacilityMapModel(fullMapModel.value, layerState))
|
|
|
-const mapEmpty = computed(() => dataLoaded.value && !mapError.value && visibleMapModel.value.isEmpty)
|
|
|
|
|
-const detailTitle = computed(() => `${labelMap[detailTarget.value?.type] || '供水设施'}详情`)
|
|
|
|
|
-const detailEntries = computed(() => Object.entries(detailRecord.value || {})
|
|
|
|
|
- .filter(([, value]) => value !== null && value !== undefined && typeof value !== 'object')
|
|
|
|
|
- .map(([label, value]) => ({ label, value: value === '' ? '-' : value })))
|
|
|
|
|
|
|
+const visibleFeatures = computed(() => [
|
|
|
|
|
+ ...visibleMapModel.value.points.map(item => ({ ...item, kind: 'point' })),
|
|
|
|
|
+ ...visibleMapModel.value.lines.map(item => ({ ...item, kind: 'pipeline' }))
|
|
|
|
|
+])
|
|
|
|
|
+
|
|
|
|
|
+const filteredFeatures = computed(() => {
|
|
|
|
|
+ const keyword = searchKeyword.value.trim().toLocaleLowerCase()
|
|
|
|
|
+ return visibleFeatures.value.filter((feature) => {
|
|
|
|
|
+ const matchedType = !selectedFacilityType.value || feature.type === selectedFacilityType.value
|
|
|
|
|
+ const matchedKeyword = !keyword || [feature.name, feature.code, feature.road]
|
|
|
|
|
+ .some(value => String(value || '').toLocaleLowerCase().includes(keyword))
|
|
|
|
|
+ return matchedType && matchedKeyword
|
|
|
|
|
+ })
|
|
|
|
|
+})
|
|
|
|
|
|
|
|
const mapPoints = computed(() => visibleMapModel.value.points.map(point => ({
|
|
const mapPoints = computed(() => visibleMapModel.value.points.map(point => ({
|
|
|
...point,
|
|
...point,
|
|
@@ -231,22 +232,28 @@ const mapLines = computed(() => visibleMapModel.value.lines.map(line => ({
|
|
|
name: line.name || '供水管网'
|
|
name: line.name || '供水管网'
|
|
|
})))
|
|
})))
|
|
|
|
|
|
|
|
|
|
+const detailEntries = computed(() => Object.entries(detailRecord.value || {})
|
|
|
|
|
+ .filter(([, value]) => value !== null && value !== undefined && typeof value !== 'object')
|
|
|
|
|
+ .map(([label, value]) => ({ label, value: value === '' ? '-' : value })))
|
|
|
|
|
+
|
|
|
watch(layerState, () => {
|
|
watch(layerState, () => {
|
|
|
- gisMapRef.value?.fitBounds()
|
|
|
|
|
|
|
+ if (mapReady.value) nextTick(() => gisMapRef.value?.fitBounds())
|
|
|
}, { deep: true })
|
|
}, { deep: true })
|
|
|
|
|
|
|
|
onMounted(async () => {
|
|
onMounted(async () => {
|
|
|
await nextTick()
|
|
await nextTick()
|
|
|
await loadGisData()
|
|
await loadGisData()
|
|
|
- document.addEventListener('fullscreenchange', handleFullscreenChange)
|
|
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
-onBeforeUnmount(() => {
|
|
|
|
|
- document.removeEventListener('fullscreenchange', handleFullscreenChange)
|
|
|
|
|
-})
|
|
|
|
|
|
|
+function handleMapReady() {
|
|
|
|
|
+ mapReady.value = true
|
|
|
|
|
+ if (dataLoaded.value) nextTick(() => gisMapRef.value?.fitBounds())
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
async function loadGisData() {
|
|
async function loadGisData() {
|
|
|
|
|
+ loading.value = true
|
|
|
mapError.value = ''
|
|
mapError.value = ''
|
|
|
|
|
+ loadError.value = ''
|
|
|
dataLoaded.value = false
|
|
dataLoaded.value = false
|
|
|
try {
|
|
try {
|
|
|
const [overviewRes, featureRes] = await Promise.all([
|
|
const [overviewRes, featureRes] = await Promise.all([
|
|
@@ -261,17 +268,23 @@ async function loadGisData() {
|
|
|
pointData.value = model.points
|
|
pointData.value = model.points
|
|
|
dataLoaded.value = true
|
|
dataLoaded.value = true
|
|
|
await nextTick()
|
|
await nextTick()
|
|
|
- gisMapRef.value?.fitBounds()
|
|
|
|
|
|
|
+ if (mapReady.value) gisMapRef.value?.fitBounds()
|
|
|
} catch (e) {
|
|
} catch (e) {
|
|
|
console.error('加载供水GIS数据失败', e)
|
|
console.error('加载供水GIS数据失败', e)
|
|
|
facilityOverview.value = []
|
|
facilityOverview.value = []
|
|
|
pipelineData.value = []
|
|
pipelineData.value = []
|
|
|
pointData.value = []
|
|
pointData.value = []
|
|
|
dataLoaded.value = true
|
|
dataLoaded.value = true
|
|
|
- mapError.value = getRequestErrorMessage(e, '供水GIS数据加载失败,请重试。')
|
|
|
|
|
|
|
+ loadError.value = getRequestErrorMessage(e, '供水GIS数据加载失败,请重试。')
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ loading.value = false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function fitBounds() {
|
|
|
|
|
+ gisMapRef.value?.fitBounds()
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function handleSearch() {
|
|
function handleSearch() {
|
|
|
const keyword = searchKeyword.value.trim()
|
|
const keyword = searchKeyword.value.trim()
|
|
|
if (!keyword) {
|
|
if (!keyword) {
|
|
@@ -291,6 +304,11 @@ function handleSearch() {
|
|
|
ElMessage.success(`已定位到 ${matched.name}`)
|
|
ElMessage.success(`已定位到 ${matched.name}`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function locateFeature(feature) {
|
|
|
|
|
+ gisMapRef.value?.locatePoint(feature)
|
|
|
|
|
+ openFacilityDetail(feature)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
async function openFacilityDetail(feature) {
|
|
async function openFacilityDetail(feature) {
|
|
|
detailTarget.value = feature
|
|
detailTarget.value = feature
|
|
|
detailOpen.value = true
|
|
detailOpen.value = true
|
|
@@ -321,357 +339,231 @@ async function refreshScene() {
|
|
|
layerState.pipe = true
|
|
layerState.pipe = true
|
|
|
layerState.user = true
|
|
layerState.user = true
|
|
|
searchKeyword.value = ''
|
|
searchKeyword.value = ''
|
|
|
|
|
+ selectedFacilityType.value = ''
|
|
|
detailOpen.value = false
|
|
detailOpen.value = false
|
|
|
await loadGisData()
|
|
await loadGisData()
|
|
|
- locateCenter()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function locateCenter() {
|
|
|
|
|
- gisMapRef.value?.locatePoint(centerPoint)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function zoomIn() {
|
|
|
|
|
- gisMapRef.value?.zoomIn()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function zoomOut() {
|
|
|
|
|
- gisMapRef.value?.zoomOut()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function toggleFullscreen() {
|
|
|
|
|
- const target = screenRef.value
|
|
|
|
|
- if (!target) return
|
|
|
|
|
- if (!document.fullscreenElement) {
|
|
|
|
|
- target.requestFullscreen?.()
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- document.exitFullscreen()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function handleFullscreenChange() {
|
|
|
|
|
- gisMapRef.value?.locatePoint(centerPoint)
|
|
|
|
|
|
|
+ fitBounds()
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
|
|
|
|
|
|
-
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
|
-.gis-dashboard {
|
|
|
|
|
- height: calc(100vh - 84px);
|
|
|
|
|
- padding: 16px;
|
|
|
|
|
- background: #f2f5f9;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- gap: 0;
|
|
|
|
|
|
|
+.water-device-gis {
|
|
|
|
|
+ min-height: 100%;
|
|
|
|
|
+ padding: 18px;
|
|
|
|
|
+ background: linear-gradient(180deg, #edf6ff 0%, #f5f7fb 100%);
|
|
|
|
|
+ color: #1f3b57;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.dashboard-header {
|
|
|
|
|
|
|
+.gis-header {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
justify-content: space-between;
|
|
|
- gap: 16px;
|
|
|
|
|
- padding: 10px 16px;
|
|
|
|
|
- margin-bottom: 14px;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- box-shadow: 0 4px 16px rgba(15, 35, 95, 0.05);
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.header-title {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 20px;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.title-main {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
- color: #1f2d3d;
|
|
|
|
|
- font-size: 17px;
|
|
|
|
|
- font-weight: 700;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.title-metrics {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-wrap: wrap;
|
|
|
|
|
- gap: 16px;
|
|
|
|
|
- margin: 8px 0;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- margin-bottom: 8px;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.metric {
|
|
|
|
|
- display: inline-flex;
|
|
|
|
|
- align-items: center; /* 垂直居中 */
|
|
|
|
|
- padding: 12px 16px; /* 左右内边距 */
|
|
|
|
|
- height: auto;
|
|
|
|
|
- min-height: 48px;
|
|
|
|
|
- font-size: 16px;
|
|
|
|
|
- line-height: 1.5;
|
|
|
|
|
- border-radius: 8px;
|
|
|
|
|
- min-width: 100px;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- background: rgba(255, 255, 255, 0.06);
|
|
|
|
|
- border: 1px solid rgba(255, 255, 255, 0.12);
|
|
|
|
|
- color: #ecf6ff;
|
|
|
|
|
- box-shadow: 0 1px 8px rgba(0, 0, 0, 0.25);
|
|
|
|
|
- transition: all 0.25s ease;
|
|
|
|
|
- cursor: default; /* 如果希望可点击,可改 pointer */
|
|
|
|
|
- backdrop-filter: blur(4px); /* 毛玻璃效果(可选,需浏览器支持) */
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-.metric::before {
|
|
|
|
|
- content: '';
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- left: 0;
|
|
|
|
|
- top: 50%;
|
|
|
|
|
- width: 9px;
|
|
|
|
|
- height: 9px;
|
|
|
|
|
- border-radius: 50%;
|
|
|
|
|
- transform: translateY(-50%);
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-/* 语义颜色强化 */
|
|
|
|
|
-.metric-source {
|
|
|
|
|
- background: rgba(69, 123, 157, 0.8);
|
|
|
|
|
- border-color: rgba(69, 123, 157, 0.6);
|
|
|
|
|
- color: #F0F4F6;
|
|
|
|
|
-}
|
|
|
|
|
-.metric-plant {
|
|
|
|
|
- background: rgba(42, 157, 143, 0.8);
|
|
|
|
|
- border-color: rgba(42, 157, 143, 0.6);
|
|
|
|
|
- color: #F0F4F6;
|
|
|
|
|
-}
|
|
|
|
|
-.metric-pumpStation {
|
|
|
|
|
- background: rgba(231, 111, 81, 0.8);
|
|
|
|
|
- border-color: rgba(231, 111, 81, 0.6);
|
|
|
|
|
- color: #F0F4F6;
|
|
|
|
|
|
|
+ gap: 18px;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
}
|
|
}
|
|
|
-.metric-pipe {
|
|
|
|
|
- background: rgba(252, 191, 73, 0.8);
|
|
|
|
|
- border-color: rgba(252, 191, 73, 0.6);
|
|
|
|
|
- color: #F0F4F6;
|
|
|
|
|
|
|
+.eyebrow {
|
|
|
|
|
+ margin: 0 0 3px;
|
|
|
|
|
+ font-size: 11px;
|
|
|
|
|
+ letter-spacing: .12em;
|
|
|
|
|
+ color: #5c93c7;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.metric:hover {
|
|
|
|
|
- transform: translateY(-2px);
|
|
|
|
|
- background: rgba(0, 149, 253, 0.5);
|
|
|
|
|
- border-color: rgba(0, 149, 253, 0.5);
|
|
|
|
|
- box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
|
|
|
|
|
|
|
+.gis-header h2 {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 22px;
|
|
|
|
|
+ color: #173a5e;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
.header-actions {
|
|
.header-actions {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
gap: 10px;
|
|
gap: 10px;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.search-input {
|
|
|
|
|
- width: 300px;
|
|
|
|
|
|
|
+.search-input,
|
|
|
|
|
+.type-select {
|
|
|
|
|
+ width: 240px;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.dashboard-content {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- gap: 14px;
|
|
|
|
|
- min-height: 0;
|
|
|
|
|
|
|
+.gis-layout {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: 310px minmax(0, 1fr);
|
|
|
|
|
+ gap: 16px;
|
|
|
|
|
+ align-items: start;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.dashboard-sidebar {
|
|
|
|
|
- width: 320px;
|
|
|
|
|
|
|
+.side-panel,
|
|
|
|
|
+.map-panel {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ height: calc(100vh - 140px);
|
|
|
|
|
+ min-height: 620px;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+}
|
|
|
|
|
+.side-panel {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
|
gap: 14px;
|
|
gap: 14px;
|
|
|
- flex-shrink: 0;
|
|
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
+ padding: 16px;
|
|
|
|
|
+ overflow: visible;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.panel-card {
|
|
|
|
|
- border: none;
|
|
|
|
|
- border-radius: 14px;
|
|
|
|
|
- box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05);
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
|
|
+.metric-grid {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: repeat(2, 1fr);
|
|
|
|
|
+ gap: 8px;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.layer-group {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- gap: 9px;
|
|
|
|
|
- padding-bottom: 16px;
|
|
|
|
|
- margin-bottom: 16px;
|
|
|
|
|
- border-bottom: 1px solid #eef2f8;
|
|
|
|
|
|
|
+.metric {
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ border: 1px solid #dceaf7;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ background: #f7fbff;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.layer-group-last {
|
|
|
|
|
- padding-bottom: 0;
|
|
|
|
|
- margin-bottom: 0;
|
|
|
|
|
- border-bottom: none;
|
|
|
|
|
|
|
+.metric span {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #668099;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.layer-title {
|
|
|
|
|
- color: #303133;
|
|
|
|
|
|
|
+.metric strong {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-top: 3px;
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ color: #1b5c98;
|
|
|
|
|
+}
|
|
|
|
|
+.panel-section h3 {
|
|
|
|
|
+ margin: 0 0 8px;
|
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
|
- font-weight: 600;
|
|
|
|
|
|
|
+ color: #29567e;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.legend-list {
|
|
|
|
|
|
|
+.layer-list {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
|
- gap: 10px;
|
|
|
|
|
|
|
+ gap: 6px;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.legend-item {
|
|
|
|
|
|
|
+.layer-row {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- gap: 8px;
|
|
|
|
|
- color: #4e5969;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.legend-line {
|
|
|
|
|
- width: 34px;
|
|
|
|
|
- height: 0;
|
|
|
|
|
- border-top-width: 3px;
|
|
|
|
|
- border-top-style: solid;
|
|
|
|
|
- border-radius: 999px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
|
|
+.legend-panel {
|
|
|
|
|
+ border-top: 1px solid #e4eef8;
|
|
|
|
|
+ padding-top: 12px;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.legend-high { border-top-color: #e63946; }
|
|
|
|
|
-.legend-sub-high { border-top-color: #f77f00; }
|
|
|
|
|
-.legend-medium { border-top-color: #fcbf49; }
|
|
|
|
|
-.legend-low { border-top-color: #2a9d8f; }
|
|
|
|
|
-
|
|
|
|
|
-.legend-dot,
|
|
|
|
|
-.legend-square {
|
|
|
|
|
- width: 12px;
|
|
|
|
|
- height: 12px;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
|
|
+.legend-row {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ gap: 8px;
|
|
|
|
|
+ margin-bottom: 6px;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+ color: #4d667d;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.legend-dot { border-radius: 50%; }
|
|
|
|
|
-.legend-square { border-radius: 2px; }
|
|
|
|
|
-.legend-valve { background: #457b9d; }
|
|
|
|
|
-.legend-regulator { background: #e76f51; }
|
|
|
|
|
-.legend-flowmeter { background: #2d6a4f; }
|
|
|
|
|
-.legend-terminal { background: #6c757d; }
|
|
|
|
|
-.legend-cover { background: #7f4f24; }
|
|
|
|
|
-.legend-inspection { background: #b08968; }
|
|
|
|
|
-
|
|
|
|
|
-.stats-card {
|
|
|
|
|
- background: linear-gradient(180deg, #f8fbff 0%, #f3f7fb 100%);
|
|
|
|
|
|
|
+.legend-dot {
|
|
|
|
|
+ width: 10px;
|
|
|
|
|
+ height: 10px;
|
|
|
|
|
+ border-radius: 50%;
|
|
|
|
|
+ flex-shrink: 0;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.stats-list {
|
|
|
|
|
|
|
+.result-section {
|
|
|
|
|
+ flex: none;
|
|
|
|
|
+ min-height: 220px;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
flex-direction: column;
|
|
|
- gap: 11px;
|
|
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.stats-row {
|
|
|
|
|
|
|
+.device-result-list {
|
|
|
|
|
+ flex: none;
|
|
|
display: flex;
|
|
display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+ overflow-y: visible;
|
|
|
|
|
+}
|
|
|
|
|
+.device-result-list button {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: minmax(0, 1fr) auto;
|
|
|
|
|
+ gap: 3px 8px;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
- justify-content: space-between;
|
|
|
|
|
- color: #4e5969;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
|
|
+ border: 1px solid #dbe8f5;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ border-radius: 8px;
|
|
|
|
|
+ padding: 8px 10px;
|
|
|
|
|
+ text-align: left;
|
|
|
|
|
+ cursor: pointer;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.stats-row strong {
|
|
|
|
|
- color: #1f2d3d;
|
|
|
|
|
- font-size: 14px;
|
|
|
|
|
|
|
+.device-name {
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ text-overflow: ellipsis;
|
|
|
|
|
+ white-space: nowrap;
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+ color: #274b6d;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.detail-card {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-height: 280px;
|
|
|
|
|
|
|
+.device-meta {
|
|
|
|
|
+ grid-column: 1 / -1;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #7793aa;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.dashboard-map-panel {
|
|
|
|
|
|
|
+.map-panel {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
- flex: 1;
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- background: #fff;
|
|
|
|
|
- border-radius: 14px;
|
|
|
|
|
overflow: hidden;
|
|
overflow: hidden;
|
|
|
- box-shadow: 0 4px 18px rgba(15, 35, 95, 0.05);
|
|
|
|
|
- min-width: 0;
|
|
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.map-toolbar {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: space-between;
|
|
|
|
|
- gap: 12px;
|
|
|
|
|
- padding: 10px 16px;
|
|
|
|
|
- border-bottom: 1px solid #edf2f7;
|
|
|
|
|
- flex-shrink: 0;
|
|
|
|
|
|
|
+.map-error {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 16px;
|
|
|
|
|
+ bottom: 16px;
|
|
|
|
|
+ width: 360px;
|
|
|
|
|
+ z-index: 1;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.toolbar-left {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
- gap: 8px;
|
|
|
|
|
|
|
+.map-empty {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
|
|
+ z-index: 1;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.toolbar-right {
|
|
|
|
|
- color: #7a8699;
|
|
|
|
|
- font-size: 13px;
|
|
|
|
|
|
|
+.device-detail {
|
|
|
|
|
+ min-height: 240px;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.gis-map {
|
|
|
|
|
- flex: 1;
|
|
|
|
|
- min-height: 0;
|
|
|
|
|
- background: #e9f0e5;
|
|
|
|
|
|
|
+.detail-hero h3,
|
|
|
|
|
+.detail-section h4 {
|
|
|
|
|
+ margin: 0 0 8px;
|
|
|
|
|
+ color: #1c4b78;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.map-error {
|
|
|
|
|
- position: absolute;
|
|
|
|
|
- inset: 52px 0 0;
|
|
|
|
|
|
|
+.detail-hero p {
|
|
|
|
|
+ margin: 0 0 10px;
|
|
|
|
|
+ color: #678197;
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+.status-tags {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
- align-items: center;
|
|
|
|
|
- justify-content: center;
|
|
|
|
|
- color: #909399;
|
|
|
|
|
- background: rgba(255, 255, 255, 0.85);
|
|
|
|
|
- font-size: 15px;
|
|
|
|
|
- z-index: 10;
|
|
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ gap: 6px;
|
|
|
|
|
+}
|
|
|
|
|
+.detail-section {
|
|
|
|
|
+ margin-top: 18px;
|
|
|
|
|
+ padding-top: 12px;
|
|
|
|
|
+ border-top: 1px solid #e5eef7;
|
|
|
|
|
+}
|
|
|
|
|
+.detail-section dl {
|
|
|
|
|
+ display: grid;
|
|
|
|
|
+ grid-template-columns: 88px minmax(0, 1fr);
|
|
|
|
|
+ gap: 7px 10px;
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ font-size: 13px;
|
|
|
}
|
|
}
|
|
|
-</style>
|
|
|
|
|
-
|
|
|
|
|
-<style>
|
|
|
|
|
-.pipe-label {
|
|
|
|
|
- padding: 2px 8px;
|
|
|
|
|
- font-size: 12px;
|
|
|
|
|
- font-weight: 600;
|
|
|
|
|
- line-height: 1.3;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- text-shadow: 0 0 4px #fff, 0 0 4px #fff;
|
|
|
|
|
- background: rgba(255, 255, 255, 0.7);
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
|
|
+.detail-section dt {
|
|
|
|
|
+ color: #71889c;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.marker-wrap {
|
|
|
|
|
- display: flex;
|
|
|
|
|
- flex-direction: column;
|
|
|
|
|
- align-items: center;
|
|
|
|
|
|
|
+.detail-section dd {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ color: #2b4c67;
|
|
|
|
|
+ overflow-wrap: anywhere;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.marker-text {
|
|
|
|
|
- margin-top: 3px;
|
|
|
|
|
- padding: 1px 7px;
|
|
|
|
|
- color: #2f3a4f;
|
|
|
|
|
- font-size: 11px;
|
|
|
|
|
- line-height: 1.3;
|
|
|
|
|
- white-space: nowrap;
|
|
|
|
|
- background: rgba(255, 255, 255, 0.78);
|
|
|
|
|
- border-radius: 10px;
|
|
|
|
|
- text-shadow: 0 0 3px #fff;
|
|
|
|
|
|
|
+.muted {
|
|
|
|
|
+ margin: 0;
|
|
|
|
|
+ color: #7d93a6;
|
|
|
|
|
+ font-size: 13px;
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
-.BMap_cpyCtrl,
|
|
|
|
|
-.anchorBL {
|
|
|
|
|
- display: none;
|
|
|
|
|
|
|
+@media (max-width: 1200px) {
|
|
|
|
|
+ .gis-layout {
|
|
|
|
|
+ grid-template-columns: 1fr;
|
|
|
|
|
+ height: auto;
|
|
|
|
|
+ }
|
|
|
|
|
+ .map-panel {
|
|
|
|
|
+ height: 560px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .search-input,
|
|
|
|
|
+ .type-select {
|
|
|
|
|
+ width: 180px;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
</style>
|
|
</style>
|