Browse Source

地图优化

master
xh 7 days ago
parent
commit
cd81bcd19c
  1. 1
      web/src/views/HandDevice/Home/components/HistoricalCurve.vue
  2. 17
      web/src/views/HandDevice/Home/components/OpenLayerMap.vue
  3. 86
      web/src/views/HandDevice/Home/components/TopPanel.vue
  4. 3
      web/src/views/HandDevice/Home/components/composables/useMapEvents.ts
  5. 6
      web/src/views/HandDevice/Home/components/composables/useMapServices.ts
  6. 3
      web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts
  7. 4
      web/src/views/HandDevice/Home/components/services/map.service.ts
  8. 56
      web/src/views/HandDevice/Home/components/services/trajectory.service.ts
  9. 28
      web/src/views/HandDevice/Home/components/utils/map.utils.ts
  10. 22
      web/src/views/HandDevice/Home/index.vue

1
web/src/views/HandDevice/Home/components/HistoricalCurve.vue

@ -97,6 +97,7 @@ async function getData(row: MarkerData) {
...item,
timeStr: dayjs(item.ts).format('YYYY-MM-DD HH:mm:ss')
}))
console.log(historicalData.value)
} catch (error) {
// ElMessage.error(error.message)
} finally {

17
web/src/views/HandDevice/Home/components/OpenLayerMap.vue

@ -96,7 +96,7 @@ const {
toggleFenceDrawing,
clearFenceDrawLayer,
updateMarkers,
refreshMarkerStyles
} = useMapServices()
const {
@ -125,7 +125,8 @@ const toggleTrajectories = () => {
showTrajectoriesStatus.value = !showTrajectoriesStatus.value
// console.log(showTrajectoriesStatus.value, props.markers);
if (showTrajectoriesStatus.value) {
setTrajectoriesVisible(showTrajectoriesStatus.value, [])
setTrajectoriesVisible(showTrajectoriesStatus.value)
}
}
@ -165,7 +166,7 @@ const init = () => {
//
setMarkersVisible(showMarkers.value)
setTrajectoriesVisible(showTrajectoriesStatus.value, [])
setTrajectoriesVisible(showTrajectoriesStatus.value)
setFencesVisible(showFences.value)
// ,, marker
@ -185,7 +186,7 @@ const init = () => {
console.log('onZoomEnd', zoom)
services.markerService?.createMarkerLayer(props)
}
// refreshMarkerStyles
}
)
//
@ -201,8 +202,6 @@ const init = () => {
setTrajectoriesVisible,
setFencesVisible,
toggleFenceDrawing,
markers: props.markers || []
})
setupAllWatchers()
@ -268,10 +267,14 @@ const showTrajectory = (startTime: number, endTime: number, markers: MarkerData[
})
])
services.mapService?.fitToMarkers(latLng)
services.trajectoryService?.setTrajectoryData(markers)
showTrajectoriesStatus.value = true
setTrajectoriesVisible(true)
console.log('showTrajectory', latLng)
setTrajectoriesVisible(true, markers)
}
}

86
web/src/views/HandDevice/Home/components/TopPanel.vue

@ -12,17 +12,17 @@
<span class="data_item__unit"></span>
</div> -->
<div class="data_item">
<span class="data_item__title">在线数量</span>
<span class="data_item__title">在线</span>
<span class="data_item__value">{{ onlineCount }} / {{ handDetectorCount }}</span>
<span class="data_item__unit"></span>
</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 class="top-right">
<!-- <span class="legend-title">报警图例</span> -->
<div class="normal-legend">正常状态</div>
<div class="alarm1-legend">围栏报警</div>
<div class="alarm2-legend">气体报警</div>
</div>
</template>
<script lang="ts" setup>
@ -50,7 +50,7 @@ var search = defineModel({
position: absolute;
top: 0px;
left: 50px;
right: 10px;
z-index: 999;
display: flex;
align-items: center;
@ -67,6 +67,7 @@ var search = defineModel({
border-radius: 10px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
height: 100%;
width: 240px;
margin-right: 12px;
padding: 10px;
}
@ -106,44 +107,47 @@ var search = defineModel({
}
}
}
}
.top-right {
position: absolute;
top: 10px;
right: 10px;
.top-panel__right {
display: flex;
align-items: center;
gap: 8px;
background: rgba(255, 255, 255, 0.85);
border: 1px solid rgba(0, 0, 0, 0.06);
padding: 12px 12px;
border-radius: 8px;
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
white-space: nowrap;
height: 100%;
display: flex;
align-items: center;
gap: 12px;
background: rgba(255, 255, 255, 0.85);
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;
.legend-title {
color: #606266;
font-size: 14px;
}
.normal-legend,
.alarm1-legend,
.alarm2-legend {
padding: 4px 8px;
border-radius: 999px;
color: #fff;
font-size: 12px;
line-height: 1;
}
.normal-legend {
background: #67c23a;
}
.legend-title {
color: #606266;
font-size: 14px;
}
.normal-legend,
.alarm1-legend,
.alarm2-legend {
padding: 4px 8px;
border-radius: 999px;
color: #fff;
font-size: 12px;
line-height: 1;
}
.alarm1-legend {
background: #e6a23c;
}
.normal-legend {
background: #67c23a;
}
.alarm2-legend {
background: #f56c6c;
}
.alarm1-legend {
background: #e6a23c;
}
.alarm2-legend {
background: #f56c6c;
}
}
</style>

3
web/src/views/HandDevice/Home/components/composables/useMapEvents.ts

@ -91,8 +91,7 @@ export const useMapEvents = () => {
isDrawing?: () => boolean
onMarkerClick?: (markerData: MarkerData) => void
onZoomEnd?: (zoom: number) => void
// markerLayer?: any
// refreshMarkerStyles?: () => void
}
) => {
if (!trajectoryService || !popupService) {

6
web/src/views/HandDevice/Home/components/composables/useMapServices.ts

@ -1,7 +1,7 @@
/**
* composable
*/
import type { Map } from 'ol'
// import type { Map } from 'ol'
import { ref, onUnmounted, reactive } from 'vue'
import type { MapProps } from '../types/map.types'
@ -82,11 +82,11 @@ export const useMapServices = () => {
/**
*
*/
const setTrajectoriesVisible = (visible: boolean, markers: any[] = []) => {
const setTrajectoriesVisible = (visible: boolean) => {
if (!services.trajectoryService) return
if (visible) {
services.trajectoryService.setTrajectoryData(markers)
// services.trajectoryService.setTrajectoryData(markers)
services.trajectoryService.show()
} else {
services.trajectoryService.hide()

3
web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts

@ -15,7 +15,6 @@ interface WatchOptions {
isDrawing: boolean,
onComplete?: (coordinates: [number, number][]) => void
) => void
markers: any[]
}
export const useMapWatchers = (options: WatchOptions) => {
@ -28,8 +27,6 @@ export const useMapWatchers = (options: WatchOptions) => {
setTrajectoriesVisible,
setFencesVisible,
toggleFenceDrawing,
markers
} = options
/**

4
web/src/views/HandDevice/Home/components/services/map.service.ts

@ -93,8 +93,8 @@ export class MapService {
const webMercatorExtent = transformExtent(extent, 'EPSG:4326', 'EPSG:3857')
this.map.getView().fit(webMercatorExtent, {
padding: [80, 50, 50, 50],
maxZoom: 15
padding: [80, 50, 100, 50],
maxZoom: this.map.getView().getMaxZoom()
})
}
/**

56
web/src/views/HandDevice/Home/components/services/trajectory.service.ts

@ -14,7 +14,13 @@ import type {
TrajectoryPlayState,
MarkerData
} from '../types/map.types'
import { createLocationIconSVG, getHighestPriorityStatus, getStatusColor } from '../utils/map.utils'
import {
createLocationIconSVG,
createStartIconSVG,
createEndIconSVG,
getHighestPriorityStatus,
getStatusColor
} from '../utils/map.utils'
import dayjs from 'dayjs'
export class TrajectoryService {
@ -36,7 +42,6 @@ export class TrajectoryService {
*
*/
createTrajectoryLayer(): VectorLayer<VectorSource> {
const source = new VectorSource()
this.trajectoryLayer = new VectorLayer({
@ -106,6 +111,20 @@ export class TrajectoryService {
})
: undefined
})
} else if (featureType === 'trajectory-start' || featureType === 'trajectory-end') {
console.log('trajectory-start', featureType)
// 开始/结束标记样式
return new Style({
image: new Icon({
src:
featureType === 'trajectory-start' ? createStartIconSVG(30) : createEndIconSVG(30), // 使用位置图标,大小为32px
scale: 1,
anchor: [0.5, 1], // 锚点设置在底部中心
anchorXUnits: 'fraction',
anchorYUnits: 'fraction'
})
})
}
return new Style()
@ -120,7 +139,7 @@ export class TrajectoryService {
const color = feature.get('color') || '#ff4757'
return new Style({
image: new Icon({
src: createLocationIconSVG(color, 32), // 使用位置图标,大小为32px
src: createLocationIconSVG(32, color), // 使用位置图标,大小为32px
scale: 1,
anchor: [0.5, 1], // 锚点设置在底部中心
anchorXUnits: 'fraction',
@ -134,7 +153,7 @@ export class TrajectoryService {
}),
offsetY: -40, // 调整文字位置,避免与图标重叠
backgroundFill: new Fill({
color: 'rgba(0, 0, 0, 0.7)'
color: 'rgba(0, 0, 0, 0.5)'
}),
padding: [2, 6, 2, 6]
})
@ -152,8 +171,8 @@ export class TrajectoryService {
*/
setTrajectoryData(markers: MarkerData[]): void {
// 从 markers 中提取轨迹数据
console.log('setTrajectoryData', markers);
console.log('setTrajectoryData', markers)
this.trajectoryData = markers
.filter((marker) => marker.data && marker.data.length > 0)
.map((marker) => ({
@ -348,8 +367,7 @@ export class TrajectoryService {
private renderTrajectories(): void {
if (!this.trajectoryLayer) return
console.log('renderTrajectories',this);
console.log('renderTrajectories', this)
const source = this.trajectoryLayer.getSource()
source?.clear()
@ -386,6 +404,27 @@ export class TrajectoryService {
})
source?.addFeature(pointFeature)
})
// 创建开始、结束标记
// 开始标记
const startFeature = new Feature({
geometry: new Point(fromLonLat(trajectory.points[0].coordinates)),
type: 'trajectory-start'
// color: trajectory.color || '#1890ff',
// pointRadius: 6, // 传递轨迹点半径
// activePointRadius: 6 // 传递激活状态轨迹点半径
})
source?.addFeature(startFeature)
// 结束标记
const endFeature = new Feature({
geometry: new Point(
fromLonLat(trajectory.points[trajectory.points.length - 1].coordinates)
),
type: 'trajectory-end'
// color: trajectory.color || '#1890ff',
// pointRadius: 6, // 传递轨迹点半径
// activePointRadius: 6 // 传递激活状态轨迹点半径
})
source?.addFeature(endFeature)
})
}
@ -460,7 +499,6 @@ export class TrajectoryService {
*
*/
hide(): void {
if (this.trajectoryLayer) {
this.trajectoryLayer.setVisible(false)
}

28
web/src/views/HandDevice/Home/components/utils/map.utils.ts

@ -101,11 +101,35 @@ export const getLabelWithTypeValue = (type: string, value: number | undefined):
const info = findStatusInfo(STATUS_DICT[type], String(value))
return info?.label || '-'
}
/**
* SVG
*/
export const createStartIconSVG = (size: number = 24, color: string = '#1296db') => {
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`
<svg width="${size}" height="${size}" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M998.4 968.533333c-132.266667 0-209.066667-8.533333-256-21.333333v-64h51.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-51.2v-42.666667h51.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-51.2v-42.666666h51.2c12.8 0 25.6-12.8 25.6-25.6s-12.8-25.6-25.6-25.6h-51.2v-34.133334c0-12.8-12.8-25.6-25.6-25.6s-25.6 12.8-25.6 25.6v34.133334H640c-12.8 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h51.2v42.666666H640c-12.8 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h51.2v128c-8.533333-8.533333-17.066667-21.333333-25.6-29.866666v-46.933334c0-12.8-12.8-25.6-25.6-25.6s-25.6 12.8-25.6 25.6c0 38.4 0 110.933333-21.333333 136.533334-8.533333 12.8-4.266667 25.6 4.266666 34.133333 4.266667 4.266667 8.533333 4.266667 12.8 4.266667 8.533333 0 17.066667-4.266667 21.333334-12.8 8.533333-12.8 17.066667-34.133333 21.333333-51.2 51.2 46.933333 136.533333 64 341.333333 64 12.8 0 25.6-12.8 25.6-25.6s-8.533333-21.333333-21.333333-21.333334z" p-id="13416" fill="${color}"></path>
<path d="M998.4 840.533333c-12.8 0-25.6 12.8-25.6 25.6v17.066667c0 4.266667 0 4.266667-4.266667 4.266667h-68.266666l-4.266667-4.266667v-93.866667h93.866667c12.8 0 25.6-12.8 25.6-25.6v-93.866666c0-12.8-12.8-25.6-25.6-25.6h-119.466667c-12.8 0-25.6 12.8-25.6 25.6s12.8 25.6 25.6 25.6h93.866667v42.666666h-93.866667c-12.8 0-25.6 12.8-25.6 25.6v119.466667c0 17.066667 4.266667 29.866667 17.066667 38.4 8.533333 8.533333 25.6 17.066667 38.4 17.066667h68.266666c29.866667 0 55.466667-25.6 55.466667-55.466667v-17.066667c0-12.8-12.8-25.6-25.6-25.6zM516.266667 819.2c-21.333333-12.8-46.933333-8.533333-59.733334 12.8-12.8 17.066667-21.333333 34.133333-25.6 55.466667-4.266667 8.533333-8.533333 21.333333-12.8 29.866666-4.266667-8.533333-8.533333-21.333333-12.8-34.133333-25.6-72.533333-81.066667-128-179.2-200.533333C136.533333 622.933333 85.333333 524.8 85.333333 418.133333 85.333333 234.666667 234.666667 85.333333 418.133333 85.333333s332.8 149.333333 332.8 332.8c0 21.333333 0 42.666667-4.266666 64s8.533333 46.933333 34.133333 51.2c21.333333 4.266667 46.933333-8.533333 51.2-34.133333 4.266667-25.6 8.533333-55.466667 8.533333-81.066667 0-230.4-187.733333-418.133333-418.133333-418.133333S0 187.733333 0 418.133333c0 136.533333 64 260.266667 174.933333 337.066667 93.866667 72.533333 132.266667 115.2 149.333334 162.133333 17.066667 46.933333 34.133333 98.133333 85.333333 102.4h8.533333c55.466667 0 72.533333-55.466667 89.6-110.933333 4.266667-12.8 8.533333-25.6 17.066667-34.133333 17.066667-17.066667 8.533333-42.666667-8.533333-55.466667z" fill="${color}"></path>
<path d="M418.133333 200.533333c-119.466667 0-213.333333 93.866667-213.333333 213.333334s93.866667 213.333333 213.333333 213.333333 213.333333-93.866667 213.333334-213.333333c0-115.2-98.133333-213.333333-213.333334-213.333334z m0 341.333334c-72.533333 0-128-55.466667-128-128s55.466667-128 128-128 128 55.466667 128 128-59.733333 128-128 128z" p-id="13418" fill="${color}"></path>
</svg>
`)}`
}
/**
* SVG
*/
export const createEndIconSVG = (size: number = 24, color: string = '#dd4e3b') => {
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`
<svg width="${size}" height="${size}" viewBox="0 0 1025 1024" xmlns="http://www.w3.org/2000/svg">
<path d="M820.049793 497.128631c4.248963-25.493776 8.497925-55.236515 8.497925-80.730291 0-229.443983-186.954357-416.39834-416.39834-416.39834S0 191.20332 0 420.647303c0 135.966805 63.73444 259.186722 174.207469 335.66805 93.477178 72.232365 131.717842 114.721992 148.713693 161.460581 16.995851 46.738589 33.991701 97.726141 84.979253 101.975103h8.497925c55.236515 0 72.232365-55.236515 89.228216-110.473029 4.248963-12.746888 8.497925-25.493776 16.995851-33.991701 12.746888-21.244813 8.497925-46.738589-12.746888-59.485477-21.244813-12.746888-46.738589-8.497925-59.485478 12.746888-12.746888 16.995851-21.244813 33.991701-25.493775 55.236514-4.248963 8.497925-8.497925 21.244813-12.746888 29.742739-4.248963-8.497925-8.497925-21.244813-12.746888-33.991701-25.493776-72.232365-80.73029-127.46888-178.456432-199.701245-84.979253-55.236515-135.966805-152.962656-135.966805-259.186722C84.979253 237.941909 233.692946 89.228216 416.39834 89.228216s331.419087 148.713693 331.419087 331.419087c0 21.244813 0 42.489627-4.248962 63.73444s8.497925 46.738589 33.991701 50.987552c16.995851 0 38.240664-12.746888 42.489627-38.240664z" fill="${color}"></path>
<path d="M416.39834 203.950207c-118.970954 0-212.448133 93.477178-212.448133 212.448133s93.477178 212.448133 212.448133 212.448133 212.448133-93.477178 212.448133-212.448133c0-114.721992-97.726141-212.448133-212.448133-212.448133z m0 339.917013c-72.232365 0-127.46888-55.236515-127.468879-127.46888s55.236515-127.46888 127.468879-127.468879 127.46888 55.236515 127.46888 127.468879-59.485477 127.46888-127.46888 127.46888zM590.605809 900.780083c4.248963 8.497925 12.746888 12.746888 21.244813 12.746888h4.248963l110.473029-29.742739c12.746888-4.248963 21.244813-16.995851 16.995851-29.742738-4.248963-12.746888-16.995851-21.244813-29.742739-16.995851l-42.489626 12.746888 59.485477-80.73029c4.248963-8.497925 8.497925-16.995851 0-29.742739s-12.746888-12.746888-21.244814-12.746888l-46.738589 4.248963 67.983403-106.224067c8.497925-12.746888 4.248963-25.493776-8.497926-33.991701-12.746888-8.497925-25.493776-4.248963-33.991701 8.497925l-101.975104 148.713693c-4.248963 8.497925-4.248963 16.995851 0 25.493776 4.248963 8.497925 12.746888 12.746888 25.493776 12.746888l42.489627-4.248962-67.983403 89.228215c0 8.497925 0 21.244813 4.248963 29.742739zM726.572614 917.775934l-118.970954 29.742738c-12.746888 4.248963-21.244813 16.995851-16.995851 29.742739 4.248963 12.746888 12.746888 21.244813 25.493776 21.244813h4.248963l118.970954-29.742739c12.746888-4.248963 21.244813-16.995851 16.995851-29.742738-4.248963-16.995851-16.995851-25.493776-29.742739-21.244813zM1002.755187 820.049793c-29.742739-8.497925-55.236515-25.493776-76.481328-42.489627 46.738589-50.987552 63.73444-101.975104 63.73444-106.224066 4.248963-8.497925 0-16.995851-4.248963-21.244814s-8.497925-8.497925-16.995851-8.497925h-80.73029c4.248963-12.746888 8.497925-21.244813 8.497925-21.244813 4.248963-12.746888-4.248963-29.742739-16.99585-33.991702-12.746888-4.248963-29.742739 4.248963-33.991702 16.995851 0 0-25.493776 67.983402-72.232365 114.721992-8.497925 8.497925-12.746888 25.493776 0 33.991701 4.248963 4.248963 12.746888 8.497925 16.995851 8.497925 4.248963 0 12.746888-4.248963 16.995851-8.497925l16.99585-16.995851c8.497925 12.746888 16.995851 25.493776 29.742739 38.240664-21.244813 12.746888-42.489627 29.742739-72.232365 38.240664-12.746888 4.248963-21.244813 21.244813-16.995851 33.991701 4.248963 8.497925 12.746888 16.995851 25.493776 16.995851h8.497925c33.991701-12.746888 63.73444-29.742739 89.228216-46.738589 25.493776 21.244813 59.485477 42.489627 97.726141 55.236514h8.497925c12.746888 0 21.244813-8.497925 25.493776-16.99585 4.248963-16.995851-4.248963-29.742739-16.99585-33.991701zM892.282158 743.568465c-16.995851-21.244813-25.493776-38.240664-33.991702-50.987552h67.983403c-8.497925 12.746888-16.995851 29.742739-33.991701 50.987552z" fill="${color}"></path>
<path d="M926.273859 926.273859h8.497925c8.497925 0 21.244813-4.248963 25.493776-16.995851 4.248963-12.746888 0-29.742739-12.746888-33.991701l-72.232365-21.244813c-12.746888-4.248963-29.742739 0-33.991701 12.746888-4.248963 12.746888 0 29.742739 12.746888 33.991701l72.232365 25.493776zM960.26556 973.012448l-101.975104-46.738589c-12.746888-4.248963-29.742739 0-33.991701 12.746888-4.248963 12.746888 0 29.742739 12.746888 33.991701l101.975104 46.738589c4.248963 0 8.497925 4.248963 8.497925 4.248963 8.497925 0 16.995851-4.248963 21.244813-16.995851 12.746888-16.995851 4.248963-29.742739-8.497925-33.991701z" fill="${color}"></path>
</svg>
`)}`
}
/**
* SVG
*/
export const createLocationIconSVG = (color: string, size: number = 24) => {
export const createLocationIconSVG = (size: number = 24,color: string='#1296db') => {
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(`
<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5c-1.38 0-2.5-1.12-2.5-2.5s1.12-2.5 2.5-2.5 2.5 1.12 2.5 2.5-1.12 2.5-2.5 2.5z" fill="${color}"/>
@ -147,7 +171,7 @@ export const createMarkerStyle = (
// 单个标记样式 - 使用位置图标
return new Style({
image: new Icon({
src: createLocationIconSVG(color, 24),
src: createLocationIconSVG(24,color),
scale: 1,
anchor: [0.5, 0.8], // 锚点设置在底部中心
anchorXUnits: 'fraction',

22
web/src/views/HandDevice/Home/index.vue

@ -1,5 +1,5 @@
<template>
<div class="flex flex-row overflow-auto" style="height: calc(100vh - 125px)">
<div class="flex flex-row overflow-auto position-relative" style="height: calc(100vh - 125px)">
<div class="flex-1 h-full position-relative">
<OpenLayerMap
ref="mapRef"
@ -399,10 +399,16 @@ onUnmounted(() => {
height: 100%;
}
.markerList {
position: absolute;
top: 60px;
right: 10px;
bottom: 10px;
width: 240px;
height: 100%;
border-radius: 8px;
// height: 100%;
overflow: hidden;
background-color: white;
background-color: rgba(255, 255, 255, 0.8);
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
padding: 0 10px;
margin-left: 10px;
@ -418,3 +424,13 @@ onUnmounted(() => {
}
}
</style>
<style>
.el-collapse-item__header {
background-color: transparent;
color: #444343;
/* background-color: rgba(0, 0, 0, 0.5); */
}
.el-collapse-item__wrap {
background-color: rgba(255, 255, 255, 0.8);
}
</style>

Loading…
Cancel
Save