Browse Source

基础轨迹

master
xh 4 days ago
parent
commit
b322e4048e
  1. 2
      web/.prettierignore
  2. 1
      web/package.json
  3. 360
      web/public/map.html
  4. 2
      web/src/components/DocAlert/index.vue
  5. 3
      web/src/store/modules/dict.ts
  6. 4
      web/src/views/HandDevice/Home/components/MapControls.vue
  7. 27
      web/src/views/HandDevice/Home/components/OpenLayerMap.vue
  8. 10
      web/src/views/HandDevice/Home/components/TopPanel.vue
  9. 2
      web/src/views/HandDevice/Home/components/TrajectoryControls.vue
  10. 21
      web/src/views/HandDevice/Home/components/composables/useMapEvents.ts
  11. 6
      web/src/views/HandDevice/Home/components/composables/useMapServices.ts
  12. 10
      web/src/views/HandDevice/Home/components/composables/useMapWatchers.ts
  13. 5
      web/src/views/HandDevice/Home/components/composables/useTrajectoryControls.ts
  14. 59
      web/src/views/HandDevice/Home/components/services/animation.service.ts
  15. 4
      web/src/views/HandDevice/Home/components/services/fence.service.ts
  16. 14
      web/src/views/HandDevice/Home/components/services/marker.service.ts
  17. 10
      web/src/views/HandDevice/Home/components/services/trajectory.service.ts
  18. 100
      web/src/views/HandDevice/Home/index.vue
  19. 48
      web/src/views/gas/fencealarm/index.vue
  20. 4
      web/src/views/gas/handalarm/HandAlarmForm.vue
  21. 4
      web/src/views/gas/handalarm/index.vue
  22. 16
      web/src/views/gas/tdengine/original.vue
  23. 2
      web/vite.config.ts

2
web/.prettierignore

@ -1,7 +1,7 @@
/node_modules/**
/dist/
/dist*
/public/*
/docs/*
/vite.config.ts
/src/types/env.d.ts

1
web/package.json

@ -2,6 +2,7 @@
"name": "HandDevice",
"version": "0.0.41",
"private": true,
"scripts": {
"i": "pnpm install",
"dev": "vite --mode dev",

360
web/public/map.html

@ -0,0 +1,360 @@
<!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>轨迹点实现</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol/ol.css" />
<style>
#map {
width: 100%;
height: 500px;
}
#controls {
padding: 10px;
background: #f5f5f5;
}
#info-panel {
padding: 10px;
background: white;
border: 1px solid #ddd;
}
.timeline {
display: flex;
overflow-x: auto;
padding: 10px 0;
}
.timeline-marker {
padding: 5px 10px;
margin: 0 5px;
background: #1890ff;
color: white;
border-radius: 3px;
cursor: pointer;
}
</style>
</head>
<body>
<div id="controls">
<button id="btn-play">播放轨迹</button>
<button id="btn-pause">暂停</button>
<div id="info-panel">轨迹信息将显示在这里</div>
</div>
<div id="map"></div>
<div class="timeline" id="timeline"></div>
<script src="https://unpkg.com/ol@9.2.4/dist/ol.js"></script>
<script>
// 轨迹数据(示例数据 - 北京到天津的轨迹)
const trackData = [
{ time: '09:00', lng: 116.404, lat: 39.915, speed: 60, name: '起点' },
{ time: '09:15', lng: 116.704, lat: 39.915, speed: 65, name: '大兴区' },
{ time: '09:30', lng: 116.904, lat: 39.815, speed: 70, name: '廊坊' },
{ time: '09:45', lng: 117.204, lat: 39.715, speed: 75, name: '武清区' },
{ time: '10:00', lng: 117.404, lat: 39.615, speed: 80, name: '天津市区' }
]
// 全局变量
let map, trackSource, trackPlayer
// 初始化地图
function initMap() {
const source = new ol.source.XYZ({
url: 'http://qtbj.icpcdev.site/roadmap/{z}/{x}/{y}.png',
maxZoom: 16,
minZoom: 10
})
map = new ol.Map({
target: 'map',
layers: [
new ol.layer.Tile({
source: source
})
],
view: new ol.View({
center: ol.proj.fromLonLat([116.404, 39.915]),
zoom: 10
})
})
// 创建轨迹图层
trackSource = new ol.source.Vector()
const trackLayer = new ol.layer.Vector({
source: trackSource,
style: function (feature) {
const type = feature.get('type')
if (type === 'track-line') {
return new ol.style.Style({
stroke: new ol.style.Stroke({
color: '#1890FF',
width: 4,
lineDash: [5, 5]
})
})
} else if (type === 'track-point') {
const speed = feature.get('speed')
return new ol.style.Style({
image: new ol.style.Circle({
radius: 6,
fill: new ol.style.Fill({
color: speed > 70 ? '#FF4D4F' : '#52C41A'
}),
stroke: new ol.style.Stroke({
color: 'white',
width: 2
})
}),
text: new ol.style.Text({
text: feature.get('name') || feature.get('time'),
offsetY: -15,
fill: new ol.style.Fill({ color: '#000' }),
stroke: new ol.style.Stroke({ color: '#fff', width: 3 })
})
})
} else if (type === 'moving-point') {
return new ol.style.Style({
image: new ol.style.Circle({
radius: 10,
fill: new ol.style.Fill({ color: '#FAAD14' }),
stroke: new ol.style.Stroke({
color: 'white',
width: 3
})
})
})
}
}
})
map.addLayer(trackLayer)
}
// 轨迹播放器类
class TrackPlayer {
constructor(trackData, source, map) {
this.trackData = trackData
this.source = source
this.map = map
this.currentIndex = 0
this.isPlaying = false
this.animationId = null
this.movingPoint = null
this.initMovingPoint()
this.createTimeline()
}
// 初始化移动点
initMovingPoint() {
this.movingPoint = new ol.Feature({
geometry: new ol.geom.Point(
ol.proj.fromLonLat([this.trackData[0].lng, this.trackData[0].lat])
),
type: 'moving-point'
})
this.source.addFeature(this.movingPoint)
}
// 创建时间轴
createTimeline() {
const timeline = document.getElementById('timeline')
timeline.innerHTML = ''
this.trackData.forEach((point, index) => {
const pointElement = document.createElement('div')
pointElement.className = 'timeline-point'
pointElement.innerHTML = `
<div>${point.time}</div>
<div>${point.name}</div>
`
pointElement.addEventListener('click', () => {
this.jumpToPoint(index)
})
timeline.appendChild(pointElement)
})
}
// 播放轨迹
play() {
if (this.isPlaying) return
this.isPlaying = true
this.currentIndex = 0
this.updateTimeline()
this.animate()
}
// 暂停播放
pause() {
this.isPlaying = false
if (this.animationId) {
cancelAnimationFrame(this.animationId)
}
}
// 跳转到指定点
jumpToPoint(index) {
this.pause()
this.currentIndex = Math.max(0, Math.min(index, this.trackData.length - 1))
this.updateMovingPoint()
this.updateTimeline()
}
// 动画循环
animate() {
if (!this.isPlaying || this.currentIndex >= this.trackData.length - 1) {
this.isPlaying = false
this.updateInfo('轨迹播放完成')
return
}
const start = this.trackData[this.currentIndex]
const end = this.trackData[this.currentIndex + 1]
this.moveToNextPoint(start, end, 0, 1500) // 1.5秒移动到下一点
}
// 移动到下一点
moveToNextPoint(start, end, progress, duration) {
if (!this.isPlaying) return
if (progress >= 1) {
this.currentIndex++
this.updateTimeline()
this.animate()
return
}
// 计算插值坐标
const lng = start.lng + (end.lng - start.lng) * progress
const lat = start.lat + (end.lat - start.lat) * progress
// 更新移动点位置
this.movingPoint.getGeometry().setCoordinates(ol.proj.fromLonLat([lng, lat]))
// 更新信息面板
this.updateInfo(end, progress)
// 平滑跟随移动点
if (progress > 0.3) {
// 延迟一点开始跟随
this.map.getView().animate({
center: ol.proj.fromLonLat([lng, lat]),
duration: 200,
easing: ol.easing.linear
})
}
// 继续动画
progress += 16 / duration // 基于60fps
this.animationId = requestAnimationFrame(() => {
this.moveToNextPoint(start, end, progress, duration)
})
}
// 更新移动点位置(直接跳转)
updateMovingPoint() {
const point = this.trackData[this.currentIndex]
this.movingPoint.getGeometry().setCoordinates(ol.proj.fromLonLat([point.lng, point.lat]))
this.map.getView().animate({
center: ol.proj.fromLonLat([point.lng, point.lat]),
zoom: 14,
duration: 500
})
}
// 更新信息面板
updateInfo(point, progress) {
const progressText = progress ? `进度: ${Math.round(progress * 100)}%` : ''
document.getElementById('info-panel').innerHTML = `
<div><strong>当前位置:</strong> ${point.name}</div>
<div><strong>时间:</strong> ${point.time}</div>
<div><strong>速度:</strong> ${point.speed} km/h</div>
<div><strong>${progressText}</strong></div>
`
}
// 更新时间轴高亮
updateTimeline() {
const points = document.querySelectorAll('.timeline-point')
points.forEach((point, index) => {
point.classList.toggle('active', index === this.currentIndex)
})
}
// 重置轨迹
reset() {
this.pause()
this.currentIndex = 0
this.updateMovingPoint()
this.updateTimeline()
this.updateInfo(this.trackData[0], 0)
}
}
// 添加轨迹到地图
function addTrackToMap() {
// 清空现有轨迹
trackSource.clear()
// 添加轨迹点
trackData.forEach((point, index) => {
const feature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([point.lng, point.lat])),
type: 'track-point',
time: point.time,
speed: point.speed,
name: point.name,
index: index
})
trackSource.addFeature(feature)
})
// 添加轨迹线
const lineCoords = trackData.map((p) => ol.proj.fromLonLat([p.lng, p.lat]))
const lineFeature = new ol.Feature({
geometry: new ol.geom.LineString(lineCoords),
type: 'track-line'
})
trackSource.addFeature(lineFeature)
// 自适应缩放
const extent = trackSource.getExtent()
map.getView().fit(extent, {
padding: [50, 50, 50, 50],
duration: 1000
})
// 初始化播放器
trackPlayer = new TrackPlayer(trackData, trackSource, map)
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function () {
// 初始化地图
initMap()
// 绑定按钮事件
// document.getElementById('btn-add-track').addEventListener('click', addTrackToMap);
document.getElementById('btn-play').addEventListener('click', function () {
if (trackPlayer) trackPlayer.play()
})
// document.getElementById('btn-play')
// document.getElementById('btn-pause').addEventListener('click', function() {
// if (trackPlayer) trackPlayer.pause();
// });
// document.getElementById('btn-reset').addEventListener('click', function() {
// if (trackPlayer) trackPlayer.reset();
// });
// document.getElementById('btn-clear').addEventListener('click', function() {
// trackSource.clear();
// document.getElementById('timeline').innerHTML = '';
// document.getElementById('info-panel').innerHTML = '轨迹已清空';
// });
// trackPlayer.play();
// 页面加载后自动添加示例轨迹
setTimeout(addTrackToMap, 1500)
})
</script>
</body>
</html>

2
web/src/components/DocAlert/index.vue

@ -5,7 +5,7 @@
</template>
</el-alert>
</template>
<script setup lang="tsx">
<script setup>
import { propTypes } from '@/utils/propTypes'
defineOptions({ name: 'DocAlert' })

3
web/src/store/modules/dict.ts

@ -48,7 +48,7 @@ export const useDictStore = defineStore('dict', {
const res = await getSimpleDictDataList()
// 设置数据
const dictDataMap = new Map<string, any>()
console.log(res)
// console.log(res)
res.forEach((dictData: DictDataVO) => {
// 获得 dictType 层级
@ -64,6 +64,7 @@ export const useDictStore = defineStore('dict', {
cssClass: dictData.cssClass
})
})
// console.log(dictDataMap)
this.dictMap = dictDataMap
this.isSetDict = true
wsCache.set(CACHE_KEY.DICT_CACHE, dictDataMap, { exp: 60 }) // 60 秒 过期

4
web/src/views/HandDevice/Home/components/MapControls.vue

@ -86,9 +86,9 @@ defineEmits<Emits>()
<style scoped>
.map-controls {
position: absolute;
padding-left: 20px;
/* padding-left: 0px; */
top: 150px;
left: 20px;
left: 10px;
display: flex;
flex-direction: column;
gap: 8px;

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

@ -7,7 +7,7 @@
:show-draw-fences="props.showDrawFences"
:is-markers-active="showMarkers"
:is-fences-active="showFences"
:is-trajectories-active="showTrajectories"
:is-trajectories-active="showTrajectoriesStatus"
:is-draw-fences-active="showDrawFences"
@toggle-markers="toggleMarkers"
@toggle-fences="toggleFences"
@ -15,7 +15,7 @@
@toggle-draw-fences="toggleDrawFences"
/>
<TrajectoryControls
:show-controls="showTrajectories"
:show-controls="showTrajectoriesStatus"
:play-state="trajectoryPlayState"
@play="playTrajectory"
@pause="pauseTrajectory"
@ -84,7 +84,7 @@ const emit = defineEmits<{
}>()
//
const showMarkers = ref(props.showMarkers)
const showTrajectories = ref(false)
const showTrajectoriesStatus = ref(false)
const showFences = ref(props.showFences)
const showDrawFences = ref(false)
const mapContainerRef = ref<HTMLElement | null>(null)
@ -132,10 +132,17 @@ const { setupMapEventListeners } = useMapEvents()
//
const toggleTrajectories = () => {
if (showTrajectories.value && trajectoryPlayState.value.isPlaying) {
if (showTrajectoriesStatus.value && trajectoryPlayState.value.isPlaying) {
cleanupTrajectory()
}
showTrajectories.value = !showTrajectories.value
showTrajectoriesStatus.value = !showTrajectoriesStatus.value
// console.log(showTrajectoriesStatus.value, props.markers);
if (showTrajectoriesStatus.value) {
setTrajectoriesVisible(showTrajectoriesStatus.value, props.markers)
}
}
const toggleMarkers = () => {
@ -174,7 +181,7 @@ const init = () => {
//
setMarkersVisible(showMarkers.value)
setTrajectoriesVisible(showTrajectories.value, props.markers)
setTrajectoriesVisible(showTrajectoriesStatus.value, props.markers)
setFencesVisible(showFences.value)
// ,, marker
@ -200,12 +207,12 @@ const init = () => {
}
)
//
setupTrajectoryWatcher(services.trajectoryService as any, showTrajectories)
setupTrajectoryWatcher(services.trajectoryService as any, showTrajectoriesStatus)
//
const { setupAllWatchers } = useMapWatchers({
showMarkers,
showTrajectories,
showTrajectoriesStatus:showTrajectoriesStatus,
showFences,
showDrawFences,
setMarkersVisible,
@ -271,7 +278,7 @@ watch(
)
watch(
() => props.fences,
(newFences) => {
() => {
refreshFences()
},
{ deep: true, immediate: false }
@ -283,7 +290,7 @@ onMounted(() => {
}, 100)
})
defineExpose({ refreshFences,fitToMarkers })
defineExpose({ refreshFences, fitToMarkers })
</script>
<style scoped lang="scss">

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

@ -6,14 +6,14 @@
</div>
</div>
<div class="top-panel__center">
<div class="data_item">
<!-- <div class="data_item">
<span class="data_item__title">手持设备</span>
<span class="data_item__value">{{ handDetectorCount }}</span>
<span class="data_item__unit"></span>
</div>
</div> -->
<div class="data_item">
<span class="data_item__title">在线数量</span>
<span class="data_item__value">{{ onlineCount }}</span>
<span class="data_item__value">{{ onlineCount }} / {{ handDetectorCount }}</span>
<span class="data_item__unit"></span>
</div>
</div>
@ -78,11 +78,11 @@ var search = defineModel({
.data_item {
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
width: 200px;
// width: 200px;
background: rgba(255, 255, 255, 0.9);
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 8px;
padding: 12px 12px;
padding: 12px 20px;
margin-right: 12px;
.data_item__title {

2
web/src/views/HandDevice/Home/components/TrajectoryControls.vue

@ -273,7 +273,7 @@ watch(
<style scoped>
.trajectory-controls {
position: absolute;
bottom: 50px;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
background: rgba(255, 255, 255, 0.95);

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

@ -17,13 +17,12 @@ interface PopupContentGenerator {
handleFence: (feature: FeatureLike) => string
handleMarker: (feature: FeatureLike) => string
}
export const useMapEvents = () => {
// 创建弹窗内容生成器
const createPopupContentGenerator = (
trajectoryService: TrajectoryService,
popupService: PopupService
): PopupContentGenerator => ({
// 创建弹窗内容生成器
function createPopupContentGenerator(
trajectoryService: TrajectoryService,
popupService: PopupService
): PopupContentGenerator {
return {
handleTrajectoryPoint: (feature: FeatureLike): string => {
const timeText = feature.get('timeText') || ''
const trajectoryId = feature.get('trajectoryId') || ''
@ -76,8 +75,10 @@ export const useMapEvents = () => {
handleMarker: (feature: FeatureLike): string => {
return popupService?.handlePopupContent(feature) || ''
}
})
}
}
export const useMapEvents = () => {
/**
*
*/
@ -97,7 +98,7 @@ export const useMapEvents = () => {
if (!trajectoryService || !popupService) {
return
}
const popupGenerator = createPopupContentGenerator(trajectoryService, popupService)
// 鼠标悬停事件
@ -190,7 +191,7 @@ export const useMapEvents = () => {
popupContent = popupGenerator.handleMarker(feature)
break
}
// if (!popupContent) return
popupElement.innerHTML = popupContent
popupOverlay.setPosition(event.coordinate)
}

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

@ -87,9 +87,9 @@ export const useMapServices = () => {
if (visible) {
services.trajectoryService.setTrajectoryData(markers)
services.trajectoryService.showTrajectories()
services.trajectoryService.show()
} else {
services.trajectoryService.hideTrajectories()
services.trajectoryService.hide()
}
}
@ -139,7 +139,7 @@ export const useMapServices = () => {
const map = services.mapService?.getMap()
const enableCluster = currentProps?.enableCluster ?? true
if (map) {
console.log('updateMarkers', markers)
// console.log('updateMarkers', markers)
// 更新marker service(这会创建新的layer)
services.markerService.createMarkerLayer({
...currentProps,

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

@ -5,7 +5,7 @@ import { watch, type Ref } from 'vue'
interface WatchOptions {
showMarkers: Ref<boolean>
showTrajectories: Ref<boolean>
showTrajectoriesStatus: Ref<boolean>
showFences: Ref<boolean>
showDrawFences: Ref<boolean>
setMarkersVisible: (visible: boolean) => void
@ -21,7 +21,7 @@ interface WatchOptions {
export const useMapWatchers = (options: WatchOptions) => {
const {
showMarkers,
showTrajectories,
showTrajectoriesStatus,
showFences,
showDrawFences,
setMarkersVisible,
@ -39,8 +39,8 @@ export const useMapWatchers = (options: WatchOptions) => {
return watch(showMarkers, (show) => {
if (show) {
// 显示标记时,隐藏轨迹
if (showTrajectories.value) {
showTrajectories.value = false
if (showTrajectoriesStatus.value) {
showTrajectoriesStatus.value = false
}
}
setMarkersVisible(show)
@ -51,7 +51,7 @@ export const useMapWatchers = (options: WatchOptions) => {
*
*/
const setupTrajectoriesWatcher = () => {
return watch(showTrajectories, (show) => {
return watch(showTrajectoriesStatus, (show) => {
if (show) {
// 显示轨迹时,隐藏标记
if (showMarkers.value) {

5
web/src/views/HandDevice/Home/components/composables/useTrajectoryControls.ts

@ -10,9 +10,9 @@ export const useTrajectoryControls = () => {
// 轨迹播放状态
const trajectoryPlayState = ref<TrajectoryPlayState>({
isPlaying: false,
currentTime: dayjs().subtract(1, 'day').valueOf(),
currentTime: dayjs().subtract(5, 'minute').valueOf(),
speed: 1,
startTime: dayjs().subtract(1, 'day').valueOf(),
startTime: dayjs().subtract(5, 'minute').valueOf(),
endTime: dayjs().valueOf()
})
@ -23,6 +23,7 @@ export const useTrajectoryControls = () => {
*
*/
const playTrajectory = () => {
debugger
if (trajectoryPlayTimer.value) {
window.clearInterval(trajectoryPlayTimer.value)
}

59
web/src/views/HandDevice/Home/components/services/animation.service.ts

@ -15,14 +15,40 @@ import { ANIMATION_CONFIG } from '../constants/map.constants'
export class AnimationService {
private rippleLayer: VectorLayer<VectorSource> | null = null
private animationTimer: number | null = null
private map: Map | null = null
private enableCluster: boolean = true
constructor(map: Map) {
this.map = map
}
updateData(markers: MarkerData[]) {
const source = this.rippleLayer?.getSource()
if (source) {
source?.clear()
// 为每个标记添加波纹效果
markers.forEach((marker) => {
const feature = new Feature({
geometry: new Point(fromLonLat(marker.coordinates)),
markerData: marker
})
const status = getHighestPriorityStatus(marker)
const color = getStatusColor(status)
// 设置动画开始时间
feature.set('animationStart', Date.now())
feature.set('rippleColor', color)
source?.addFeature(feature)
// this.rippleLayer?.setSource(source)
})
}
}
/**
*
* enableCluster
*/
createRippleLayer(
markers: MarkerData[],
@ -30,27 +56,28 @@ export class AnimationService {
enableCluster: boolean = true
): VectorLayer<VectorSource> {
if (this.rippleLayer && this.map) {
this.map?.removeLayer(this.rippleLayer)
this.updateData(markers)
return this.rippleLayer
}
this.enableCluster = enableCluster
const source = new VectorSource()
// 为每个标记添加波纹效果
markers.forEach((marker) => {
const feature = new Feature({
geometry: new Point(fromLonLat(marker.coordinates)),
markerData: marker
})
// // 为每个标记添加波纹效果
// markers.forEach((marker) => {
// const feature = new Feature({
// geometry: new Point(fromLonLat(marker.coordinates)),
// markerData: marker
// })
const status = getHighestPriorityStatus(marker)
const color = getStatusColor(status)
// const status = getHighestPriorityStatus(marker)
// const color = getStatusColor(status)
// 设置动画开始时间
feature.set('animationStart', Date.now())
feature.set('rippleColor', color)
// // 设置动画开始时间
// feature.set('animationStart', Date.now())
// feature.set('rippleColor', color)
source.addFeature(feature)
})
// source.addFeature(feature)
// })
this.rippleLayer = new VectorLayer({
source: source,
@ -111,8 +138,8 @@ export class AnimationService {
return styles
}
})
if (this.rippleLayer && this.map) {
this.updateData(markers)
if (this.map) {
this.map?.addLayer(this.rippleLayer)
}

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

@ -109,7 +109,9 @@ export class FenceService {
lineDash: lineDash
}),
fill: new Fill({
color: fillColor
color: fillColor,
// opacity: 0.5
})
})
}

14
web/src/views/HandDevice/Home/components/services/marker.service.ts

@ -21,21 +21,23 @@ export class MarkerService {
this.map = map
this.vectorSource = new VectorSource()
}
show(){
show() {
this.markerLayer?.setVisible(true)
}
hide(){
hide() {
this.markerLayer?.setVisible(false)
}
/**
*
*/
createMarkerLayer(props: MapProps): VectorLayer<VectorSource | Cluster> {
createMarkerLayer(props: MapProps) {
// console.log('createMarkerLayer')
if (this.markerLayer) {
this.map?.removeLayer(this.markerLayer)
// this.map?.removeLayer(this.markerLayer)
this.updateData(props)
} else {
this.createMarkerLayerFromProps(props)
}
return this.createMarkerLayerFromProps(props)
}
/**
*
@ -122,7 +124,7 @@ export class MarkerService {
zIndex: 1,
renderOrder: (a, b) => {
// console.log('renderOrder',a,b.get('markerData').time);
// 按xxx属性降序排列
return b.get('markerData').time - a.get('markerData').time
}

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

@ -142,6 +142,7 @@ export class TrajectoryService {
})
this.map?.addLayer(this.movingMarkerLayer)
this.map?.addLayer(this.trajectoryLayer)
return this.trajectoryLayer
}
@ -150,6 +151,8 @@ export class TrajectoryService {
*/
setTrajectoryData(markers: MarkerData[]): void {
// 从 markers 中提取轨迹数据
console.log('setTrajectoryData', markers);
this.trajectoryData = markers
.filter((marker) => marker.data && marker.data.length > 0)
.map((marker) => ({
@ -343,6 +346,9 @@ export class TrajectoryService {
private renderTrajectories(): void {
if (!this.trajectoryLayer) return
console.log('renderTrajectories',this);
const source = this.trajectoryLayer.getSource()
source?.clear()
@ -439,7 +445,7 @@ export class TrajectoryService {
/**
* showTrajectoryControls true
*/
showTrajectories(): void {
show(): void {
if (this.trajectoryLayer) {
this.trajectoryLayer.setVisible(true)
}
@ -451,7 +457,7 @@ export class TrajectoryService {
/**
*
*/
hideTrajectories(): void {
hide(): void {
if (this.trajectoryLayer) {
this.trajectoryLayer.setVisible(false)
}

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

@ -16,10 +16,39 @@
/>
</div>
<div class="markerList">
<!--marker列表 -->
<div v-for="item in filterMarkers" :key="item.id" class="marker-item">
<el-scrollbar height="100%">
<!--marker列表 -->
<el-collapse accordion>
<el-collapse-item :name="item.name" v-for="item in filterMarkers" :key="item.id">
<template #title>
<div class="flex flex-row w-100%">
<div class="flex-1 text-left font-500">
{{ item.name }}
</div>
<div class="text-gray-500 font-400 text-12px">
{{ item.onlineStatus === 1 ? '在线' : '离线' }}
</div>
</div>
</template>
<div class="markerList-content">
<div> <span>SN</span>{{ item.sn }}</div>
<div> <span>类型</span>{{ item.gasChemical }}</div>
<div> <span>气体状态</span>{{ item.gasStatus }}</div>
<div> <span>电池告警状态</span>{{ item.batteryStatus }}</div>
<div> <span>电池</span>{{ item.battery }}</div>
<div> <span>围栏状态</span>{{ item.fenceStatus }}</div>
<!-- <div> <span>在线状态</span>{{ item.onlineStatus }}</div> -->
<div> <span>数值</span>{{ item.value }} {{ item.unit }}</div>
<div> <span>时间</span>{{ dayjs(item.time).format('YYYY-MM-DD HH:mm:ss') }}</div>
<!-- {{ item }} -->
</div>
</el-collapse-item>
</el-collapse>
</el-scrollbar>
<!-- <div v-for="item in filterMarkers" :key="item.id" class="marker-item">
<div>{{ item.name }}{{ item.onlineStatus === 1 ? '在线' : '离线' }}</div>
</div>
</div> -->
</div>
</div>
</template>
@ -29,6 +58,8 @@ import TopPanel from './components/TopPanel.vue'
import { getLastDetectorData } from '@/api/gas'
import { HandDetectorData } from '@/api/gas/handdetector'
import { tdengineApi, tdStruct, tdQuery } from '@/api/gas/tdengine/index'
import { MarkerData, FenceData } from './components/types/map.types'
import { useHandDetectorStore } from '@/store/modules/handDetector'
@ -73,7 +104,7 @@ const getMarkers = async () => {
return await getLastDetectorData().then((res: HandDetectorData[]) => {
res = res.filter((i) => i.enableStatus === 1)
var res2 = res.map((i) => {
console.log([i.longitude, i.latitude])
// console.log([i.longitude, i.latitude])
return {
...i,
@ -90,26 +121,55 @@ const getMarkers = async () => {
}
})
markers.value = res2 as unknown as any[]
getMarkersHistory()
})
}
const getFences = async () => {
return await handDetectorStore.getAllFences().then((res) => {
console.log('getFences', res)
let fencesData = res.map((i) => {
return {
...i,
fenceRange: JSON.parse(i.fenceRange)
}
})
// console.log('getFences', res)
let fencesData = res
.map((i) => {
return {
...i,
fenceRange: JSON.parse(i.fenceRange)
}
})
.filter((i) => i.status === 1)
fences.value = fencesData as unknown as FenceData[]
})
}
// const markersHistory = ref<tdStruct[]>([])
const getMarkersHistory = async () => {
try {
const data = await tdengineApi.getPage({
pageNo: 1,
pageSize: 100,
sn: '867989072729904'
})
// markersHistory.value = data.list
markers.value.map((item) => {
if (item.sn === '867989072729904') {
item.data = data.list.map((j) => {
return {
...j,
lng: j.longitude,
lat: j.latitude,
time:dayjs(j.ts).format('YYYY-MM-DD HH:mm:ss'),
timeStr: dayjs(j.ts).format('YYYY-MM-DD HH:mm:ss')
}
})
}
})
} finally {
}
}
onMounted(() => {
getMarkers()
getFences()
// getFences()
console.log('定时器,暂时关掉,太烦了')
getDataTimer.value = setInterval(() => {
@ -127,10 +187,22 @@ onUnmounted(() => {
height: 100%;
}
.markerList {
width: 220px;
width: 240px;
height: 100%;
overflow: hidden;
background-color: white;
padding:0 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
padding: 0 10px;
margin-left: 10px;
.markerList-content {
padding: 10px;
font-size: 12px;
span {
display: inline-block;
min-width: 70px;
}
}
}
</style>

48
web/src/views/gas/fencealarm/index.vue

@ -105,7 +105,7 @@
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
<el-button
<!-- <el-button
type="danger"
plain
:disabled="isEmpty(checkedIds)"
@ -113,7 +113,7 @@
v-hasPermi="['gas:fence-alarm:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
</el-button> -->
</el-form-item>
</el-form>
</ContentWrap>
@ -187,14 +187,14 @@
>
编辑
</el-button>
<el-button
<!-- <el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['gas:fence-alarm:delete']"
>
删除
</el-button>
</el-button> -->
</template>
</el-table-column>
</el-table>
@ -285,28 +285,28 @@ const openForm = (type: string, id?: number) => {
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
//
await message.delConfirm()
//
await FenceAlarmApi.deleteFenceAlarm(id)
message.success(t('common.delSuccess'))
//
await getList()
} catch {}
}
// const handleDelete = async (id: number) => {
// try {
// //
// await message.delConfirm()
// //
// await FenceAlarmApi.deleteFenceAlarm(id)
// message.success(t('common.delSuccess'))
// //
// await getList()
// } catch {}
// }
/** 批量删除GAS手持探测器围栏报警 */
const handleDeleteBatch = async () => {
try {
//
await message.delConfirm()
await FenceAlarmApi.deleteFenceAlarmList(checkedIds.value)
message.success(t('common.delSuccess'))
await getList()
} catch {}
}
// const handleDeleteBatch = async () => {
// try {
// //
// await message.delConfirm()
// await FenceAlarmApi.deleteFenceAlarmList(checkedIds.value)
// message.success(t('common.delSuccess'))
// await getList()
// } catch {}
// }
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (records: FenceAlarm[]) => {

4
web/src/views/gas/handalarm/HandAlarmForm.vue

@ -1,5 +1,5 @@
<template>
<Dialog :title="dialogTitle" v-model="dialogVisible">
<Dialog :title="dialogTitle" :scroll="true" v-model="dialogVisible">
<el-form
ref="formRef"
:model="formData"
@ -17,7 +17,7 @@
v-for="item in props.handDetector"
:key="item.id"
:label="item.name"
:value="item.id"
:value="Number(item.id)"
/>
</el-select>
</el-form-item>

4
web/src/views/gas/handalarm/index.vue

@ -225,7 +225,7 @@
/> -->
<el-table-column label="操作" align="center" width="140px">
<template #default="scope">
<el-button link type="primary" @click="openForm('update', scope.row.id)">
<!-- <el-button link type="primary" @click="openForm('update', scope.row.id)">
查看位置
</el-button>
@ -236,7 +236,7 @@
v-hasPermi="['gas:hand-alarm:update']"
>
编辑
</el-button>
</el-button> -->
<!-- <el-button
link
type="danger"

16
web/src/views/gas/tdengine/original.vue

@ -53,6 +53,7 @@
<el-table-column label="设备编号" align="center" prop="sn" width="160px" />
<el-table-column label="原始数据" align="center" prop="payload" />
<el-table-column label="定位方式" align="center" prop="gpsType" width="80px" />
<!-- <el-table-column label="气体类型" align="center" prop="gasType"></el-table-column> -->
<!-- <el-table-column label="首报值" align="center" prop="vAlarmFirst" />
@ -129,6 +130,21 @@ const getList = async () => {
loading.value = true
try {
const data = await tdengineApi.getOriginalLogPage(queryParams)
// 30GPS5500
data.list.forEach((item) => {
try {
let payload = JSON.parse(item.payload)
var loc = payload.loc as number[]
if (loc && loc.length == 3) {
item.gpsType = loc[2] === 0 ? 'GPS' : '基站'
} else {
item.gpsType = '-'
}
} catch (error) {
item.gpsType = '-'
}
})
list.value = data.list
total.value = data.total
} finally {

2
web/vite.config.ts

@ -93,6 +93,6 @@ export default ({ command, mode }: ConfigEnv): UserConfig => {
},
},
},
optimizeDeps: { include, exclude }
optimizeDeps: { include, }//exclude
}
}

Loading…
Cancel
Save