|
|
@ -24,37 +24,7 @@ |
|
|
@time-change="setTrajectoryTime" |
|
|
@time-change="setTrajectoryTime" |
|
|
@time-range-change="setTrajectoryTimeRange" |
|
|
@time-range-change="setTrajectoryTimeRange" |
|
|
/> |
|
|
/> |
|
|
<div class="top-panel" v-show="!appStore.mobile && !props.hideTopPanel"> |
|
|
|
|
|
<div class="top-panel__left"> |
|
|
|
|
|
<div class="search-group"> |
|
|
|
|
|
<el-input v-model="search" class="search-input" placeholder="请输入关键词" /> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="top-panel__center"> |
|
|
|
|
|
<div class="data_item"> |
|
|
|
|
|
<div class="data_item__title">手持设备</div> |
|
|
|
|
|
<div class="data_item__value" |
|
|
|
|
|
>{{ handDetectorCount }}<span class="data_item__unit">台</span></div |
|
|
|
|
|
> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="data_item"> |
|
|
|
|
|
<div class="data_item__title">在线数量</div> |
|
|
|
|
|
<div class="data_item__value" |
|
|
|
|
|
>{{ onlineCount }}<span class="data_item__unit">台</span></div |
|
|
|
|
|
> |
|
|
|
|
|
</div> |
|
|
|
|
|
<!-- <div class="data_item"> |
|
|
|
|
|
<div class="data_item__title">用户数量</div> |
|
|
|
|
|
<div class="data_item__value">200<span class="data_item__unit">人</span></div> |
|
|
|
|
|
</div> --> |
|
|
|
|
|
</div> |
|
|
|
|
|
<div class="top-panel__right"> |
|
|
|
|
|
<span class="legend-title">报警图例:</span> |
|
|
|
|
|
<div class="normal-legend">正常状态</div> |
|
|
|
|
|
<div class="alarm1-legend">围栏报警</div> |
|
|
|
|
|
<div class="alarm2-legend">气体报警</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<div v-if="panelVisible" class="info-panel"> |
|
|
<div v-if="panelVisible" class="info-panel"> |
|
|
<div class="info-panel__header"> |
|
|
<div class="info-panel__header"> |
|
|
<span class="info-panel__title">设备详情</span> |
|
|
<span class="info-panel__title">设备详情</span> |
|
|
@ -74,8 +44,7 @@ |
|
|
</div> |
|
|
</div> |
|
|
</template> |
|
|
</template> |
|
|
<script lang="ts" setup> |
|
|
<script lang="ts" setup> |
|
|
import { useAppStore } from '@/store/modules/app' |
|
|
|
|
|
import { useHandDetectorStore } from '@/store/modules/handDetector' |
|
|
|
|
|
|
|
|
|
|
|
import { ref, onMounted, watch } from 'vue' |
|
|
import { ref, onMounted, watch } from 'vue' |
|
|
import { ElMessage } from 'element-plus' |
|
|
import { ElMessage } from 'element-plus' |
|
|
// 导入类型定义 |
|
|
// 导入类型定义 |
|
|
@ -101,13 +70,13 @@ const props = withDefaults(defineProps<MapProps>(), { |
|
|
minZoom: MAP_DEFAULTS.minZoom, |
|
|
minZoom: MAP_DEFAULTS.minZoom, |
|
|
markers: () => DEFAULT_MARKERS, |
|
|
markers: () => DEFAULT_MARKERS, |
|
|
fences: () => DEFAULT_FENCES, |
|
|
fences: () => DEFAULT_FENCES, |
|
|
|
|
|
forceSingleMark: 13, |
|
|
enableCluster: MAP_DEFAULTS.enableCluster, |
|
|
enableCluster: MAP_DEFAULTS.enableCluster, |
|
|
clusterDistance: MAP_DEFAULTS.clusterDistance, |
|
|
clusterDistance: MAP_DEFAULTS.clusterDistance, |
|
|
showTrajectories: true, |
|
|
showTrajectories: true, |
|
|
showMarkers: true, |
|
|
showMarkers: true, |
|
|
showFences: true, |
|
|
showFences: true, |
|
|
showDrawFences: true, |
|
|
|
|
|
hideTopPanel: false |
|
|
|
|
|
|
|
|
showDrawFences: true |
|
|
}) |
|
|
}) |
|
|
|
|
|
|
|
|
const emit = defineEmits<{ |
|
|
const emit = defineEmits<{ |
|
|
@ -120,24 +89,20 @@ const showTrajectories = ref(false) |
|
|
const showFences = ref(props.showFences) |
|
|
const showFences = ref(props.showFences) |
|
|
const showDrawFences = ref(false) |
|
|
const showDrawFences = ref(false) |
|
|
const mapContainerRef = ref<HTMLElement | null>(null) |
|
|
const mapContainerRef = ref<HTMLElement | null>(null) |
|
|
const handDetectorStore = useHandDetectorStore() |
|
|
|
|
|
|
|
|
|
|
|
// 左侧信息面板状态 |
|
|
// 左侧信息面板状态 |
|
|
const appStore = useAppStore() |
|
|
|
|
|
|
|
|
|
|
|
const panelVisible = ref(false) |
|
|
const panelVisible = ref(false) |
|
|
const selectedMarker = ref<MarkerData | null>(null) |
|
|
const selectedMarker = ref<MarkerData | null>(null) |
|
|
const search = ref('') |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* gasStatus 气体报警状态 |
|
|
|
|
|
|
|
|
gasStatus 气体报警状态 |
|
|
batteryStatus 电池报警状态 |
|
|
batteryStatus 电池报警状态 |
|
|
fenceStatus 电子围栏报警状态 |
|
|
fenceStatus 电子围栏报警状态 |
|
|
onlineStatus 1在线 0离线 |
|
|
onlineStatus 1在线 0离线 |
|
|
enableStatus 1启用 0未启用 |
|
|
enableStatus 1启用 0未启用 |
|
|
*/ |
|
|
*/ |
|
|
|
|
|
|
|
|
// 手持设备数量 |
|
|
|
|
|
const handDetectorCount = computed(() => props.markers.length) |
|
|
|
|
|
const onlineCount = computed(() => props.markers.filter((item) => item.onlineStatus === 1).length) |
|
|
|
|
|
// 使用组合式函数 |
|
|
// 使用组合式函数 |
|
|
const { |
|
|
const { |
|
|
services, |
|
|
services, |
|
|
@ -196,6 +161,7 @@ const toggleDrawFences = () => { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
import { MapService } from './services/map.service' |
|
|
import { MapService } from './services/map.service' |
|
|
|
|
|
|
|
|
let mapService: MapService | null = null |
|
|
let mapService: MapService | null = null |
|
|
let isMapInitialized = false |
|
|
let isMapInitialized = false |
|
|
/** |
|
|
/** |
|
|
@ -233,6 +199,11 @@ const initMap = () => { |
|
|
selectedMarker.value = marker |
|
|
selectedMarker.value = marker |
|
|
panelVisible.value = true |
|
|
panelVisible.value = true |
|
|
}, |
|
|
}, |
|
|
|
|
|
onZoomEnd: (zoom: number) => { |
|
|
|
|
|
// console.log('onZoomEnd', zoom) |
|
|
|
|
|
// services.markerService?.updateMarkers(props.markers) |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
markerLayer: layerRefs.value?.markerLayer, |
|
|
markerLayer: layerRefs.value?.markerLayer, |
|
|
refreshMarkerStyles |
|
|
refreshMarkerStyles |
|
|
} |
|
|
} |
|
|
@ -322,157 +293,6 @@ defineExpose({ refreshFences }) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* 顶部面板样式 */ |
|
|
|
|
|
.top-panel { |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
top: 12px; |
|
|
|
|
|
left: 12px; |
|
|
|
|
|
right: 12px; |
|
|
|
|
|
z-index: 1000; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
justify-content: space-between; |
|
|
|
|
|
gap: 12px; |
|
|
|
|
|
padding: 10px 12px; |
|
|
|
|
|
flex-wrap: wrap; |
|
|
|
|
|
height: 100px; |
|
|
|
|
|
box-sizing: border-box; |
|
|
|
|
|
|
|
|
|
|
|
.top-panel__left { |
|
|
|
|
|
flex: 0 0 260px; |
|
|
|
|
|
background: rgba(255, 255, 255, 0.7); |
|
|
|
|
|
border: 1px solid rgba(0, 0, 0, 0.06); |
|
|
|
|
|
border-radius: 10px; |
|
|
|
|
|
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12); |
|
|
|
|
|
height: 100%; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
|
|
|
|
|
|
.search-group { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
gap: 8px; |
|
|
|
|
|
padding: 8px; |
|
|
|
|
|
|
|
|
|
|
|
.search-type { |
|
|
|
|
|
flex: 0 0 120px; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.search-input { |
|
|
|
|
|
width: 220px; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.top-panel__center { |
|
|
|
|
|
flex: 1 1 auto; |
|
|
|
|
|
display: grid; |
|
|
|
|
|
grid-template-columns: repeat(4, minmax(0, 1fr)); |
|
|
|
|
|
gap: 12px; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
|
|
|
|
|
|
.data_item { |
|
|
|
|
|
background: rgba(255, 255, 255, 0.85); |
|
|
|
|
|
border: 1px solid rgba(0, 0, 0, 0.06); |
|
|
|
|
|
border-radius: 8px; |
|
|
|
|
|
padding: 8px 12px; |
|
|
|
|
|
min-height: 56px; |
|
|
|
|
|
display: flex; |
|
|
|
|
|
flex-direction: column; |
|
|
|
|
|
justify-content: center; |
|
|
|
|
|
|
|
|
|
|
|
.data_item__title { |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
color: #909399; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.data_item__value { |
|
|
|
|
|
font-size: 18px; |
|
|
|
|
|
font-weight: 600; |
|
|
|
|
|
color: #303133; |
|
|
|
|
|
margin-top: 4px; |
|
|
|
|
|
|
|
|
|
|
|
.data_item__unit { |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
color: #909399; |
|
|
|
|
|
margin-left: 6px; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.top-panel__right { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
gap: 8px; |
|
|
|
|
|
flex: 0 0 auto; |
|
|
|
|
|
background: rgba(255, 255, 255, 0.7); |
|
|
|
|
|
border: 1px solid rgba(0, 0, 0, 0.06); |
|
|
|
|
|
padding: 8px 12px; |
|
|
|
|
|
border-radius: 8px; |
|
|
|
|
|
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12); |
|
|
|
|
|
white-space: nowrap; |
|
|
|
|
|
height: 100%; |
|
|
|
|
|
|
|
|
|
|
|
.legend-title { |
|
|
|
|
|
color: #606266; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.normal-legend { |
|
|
|
|
|
padding: 4px 8px; |
|
|
|
|
|
border-radius: 999px; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
line-height: 1; |
|
|
|
|
|
background: #67c23a; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.alarm1-legend { |
|
|
|
|
|
padding: 4px 8px; |
|
|
|
|
|
border-radius: 999px; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
line-height: 1; |
|
|
|
|
|
background: #e6a23c; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.alarm2-legend { |
|
|
|
|
|
padding: 4px 8px; |
|
|
|
|
|
border-radius: 999px; |
|
|
|
|
|
color: #fff; |
|
|
|
|
|
font-size: 12px; |
|
|
|
|
|
line-height: 1; |
|
|
|
|
|
background: #f56c6c; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 992px) { |
|
|
|
|
|
.top-panel { |
|
|
|
|
|
.top-panel__left { |
|
|
|
|
|
flex: 1 1 100%; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.top-panel__center { |
|
|
|
|
|
flex: 1 1 100%; |
|
|
|
|
|
grid-template-columns: repeat(2, minmax(0, 1fr)); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.top-panel__right { |
|
|
|
|
|
flex: 1 1 100%; |
|
|
|
|
|
justify-content: flex-start; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@media (max-width: 600px) { |
|
|
|
|
|
.top-panel { |
|
|
|
|
|
.top-panel__center { |
|
|
|
|
|
grid-template-columns: 1fr; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.info-panel { |
|
|
.info-panel { |
|
|
position: absolute; |
|
|
position: absolute; |
|
|
top: 100px; |
|
|
top: 100px; |
|
|
@ -548,6 +368,4 @@ defineExpose({ refreshFences }) |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</style> |
|
|
</style> |
|
|
|