This commit is contained in:
磷叶 2025-04-10 19:56:40 +08:00
parent 0207c44b44
commit a382207c64
5 changed files with 281 additions and 119 deletions

View File

@ -51,3 +51,16 @@ export function listSimpleArea(query) {
params: query
})
}
// 获取定位所属区域信息
export function getLocationArea(lon, lat, areaId) {
return request({
url: '/bst/area/locationArea',
method: 'get',
params: {
lon,
lat,
areaId
}
})
}

View File

@ -12,75 +12,22 @@
<el-button size="mini" icon="el-icon-picture" @click="toggleMapStyle">切换样式</el-button>
<el-button size="mini" icon="el-icon-document" @click="toggleLabels">{{ showLabels ? '隐藏' : '显示' }}文字</el-button>
<el-button size="mini" icon="el-icon-video-play" v-if="locationLogList && locationLogList.length > 0" @click="togglePlaybackPanel">{{ isPlaybackVisible ? '隐藏' : '显示' }}轨迹控制台</el-button>
<el-button size="mini" :type="isDebugMode ? 'danger' : ''" v-if="checkPermi(['bst:area:locationArea'])" icon="el-icon-position" @click="toggleDebugMode">{{ isDebugMode ? '退出调试' : '调试' }}</el-button>
</el-button-group>
</div>
<!-- 轨迹回放控制面板 -->
<el-card class="playback-panel" v-if="isPlaybackVisible && locationLogList.length > 0" header="轨迹控制台">
<el-row class="device-info">
<el-col :span="24" class="info-item">
<span class="label">时间</span>
<span class="value">{{ currentLog ? currentLog.at : '-' }}</span>
</el-col>
<el-col :span="24" class="info-item">
<span class="label">位置</span>
<span class="value">{{ currentLog ? `${currentLog.longitude}, ${currentLog.latitude}` : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">速度</span>
<span class="value">{{ currentSpeed }} Km/h</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">电压</span>
<span class="value">{{ currentLog ? currentLog.voltage + 'V' : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">信号</span>
<span class="value">{{ currentLog ? currentLog.signal : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">卫星</span>
<span class="value">{{ currentLog ? currentLog.satellites : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">状态</span>
<span class="value"><dict-tag :options="dict.type.device_status" :value="currentLog.status" size="mini"/></span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">锁状态</span>
<span class="value"><dict-tag :options="dict.type.device_lock_status" :value="currentLog.lockStatus" size="mini"/></span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">电门</span>
<span class="value"><dict-tag :options="dict.type.device_quality" :value="currentLog.quality" size="mini"/></span>
</el-col>
</el-row>
<div class="playback-controls">
<el-slider
v-model="currentLogIndex"
:max="locationLogList.length - 1"
:show-tooltip="false"
@input="handleSliderChange">
</el-slider>
<div class="control-buttons">
<el-button-group>
<el-button size="mini" :icon="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'" @click="togglePlay">
{{ isPlaying ? '暂停' : '播放' }}
</el-button>
<el-button
v-for="speed in [1, 2, 4, 8]"
:key="speed"
size="mini"
:type="playbackSpeed === speed ? 'primary' : ''"
@click="setPlaybackSpeed(speed)">
{{ speed }}x
</el-button>
</el-button-group>
</div>
</div>
</el-card>
<playback-panel
v-if="isPlaybackVisible && locationLogList.length > 0"
:current-log="currentLog"
:current-speed="currentSpeed"
:max-index="sortedLocationLogs.length - 1"
:is-playing="isPlaying"
:playback-speed="playbackSpeed"
@slider-change="handleSliderChange"
@toggle-play="togglePlay"
@speed-change="setPlaybackSpeed"
/>
<!-- 编辑工具栏 -->
<div class="boundary-tools" v-if="isEditing">
@ -106,9 +53,14 @@ import { AreaSubStatus } from "@/utils/enums";
import { debounce, isEmpty } from "@/utils/index";
import globalConfig from "@/utils/config/globalConfig";
import { AREA_STYLES, HIGHLIGHT_STYLES, MARKER_ICONS, LABEL_STYLES, DEVICE_STATUS_ICONS } from "@/views/bst/areaSub/components/AreaMap";
import PlaybackPanel from './PlaybackPanel.vue';
import { getLocationArea } from "@/api/bst/area";
export default {
name: 'AreaMap',
components: {
PlaybackPanel
},
dicts: ['device_status', 'device_lock_status', 'device_quality'],
props: {
//
@ -162,6 +114,8 @@ export default {
currentLog: null,
sortedLocationLogs: [],
progressPolyline: null,
isDebugMode: false, //
debugClickHandler: null, //
};
},
@ -369,6 +323,10 @@ export default {
});
polygon.on('click', (e) => {
if (this.isDebugMode) {
//
return;
}
if (!this.isEditing) {
//
if (this.selectedAreaId !== subArea.id) {
@ -394,6 +352,10 @@ export default {
});
marker.on('click', (e) => {
if (this.isDebugMode) {
//
return;
}
if (!this.isEditing) {
this.showInfoWindow(subArea);
}
@ -427,6 +389,10 @@ export default {
});
label.on('click', (e) => {
if (this.isDebugMode) {
//
return;
}
if (!this.isEditing) {
this.showInfoWindow(subArea);
}
@ -743,6 +709,7 @@ export default {
destroyMap() {
if (this.map) {
this.clearDebugMode();
this.clearHighlight();
this.clearOverlays();
this.map.destroy();
@ -1026,6 +993,65 @@ export default {
this.playbackTimer = null;
}
},
//
toggleDebugMode() {
this.isDebugMode = !this.isDebugMode;
if (this.isDebugMode) {
//
this.debugClickHandler = (e) => {
const { lng, lat } = e.lnglat;
console.log('点击位置:', lng, lat);
// API
getLocationArea(lng, lat, this.area.id).then(response => {
console.log('区域信息:', response.data);
}).catch(error => {
console.error('获取区域信息失败:', error);
});
};
//
this.overlays.polygons.forEach(polygon => {
polygon.on('click', this.debugClickHandler);
});
this.overlays.markers.forEach(marker => {
marker.on('click', this.debugClickHandler);
});
this.overlays.labels.forEach(label => {
label.on('click', this.debugClickHandler);
});
this.map.on('click', this.debugClickHandler);
this.$message.success('已进入调试模式,点击地图获取位置信息');
} else {
// 退
this.clearDebugMode();
this.$message.success('已退出调试模式');
}
},
//
clearDebugMode() {
if (this.debugClickHandler) {
//
this.map.off('click', this.debugClickHandler);
//
this.overlays.polygons.forEach(polygon => {
polygon.off('click', this.debugClickHandler);
});
this.overlays.markers.forEach(marker => {
marker.off('click', this.debugClickHandler);
});
this.overlays.labels.forEach(label => {
label.off('click', this.debugClickHandler);
});
this.debugClickHandler = null;
}
this.isDebugMode = false;
},
}
};
</script>
@ -1060,57 +1086,7 @@ export default {
text-align: left;
}
}
.playback-panel {
position: absolute;
top: 60px;
right: 15px;
transform: none;
z-index: 100;
width: 400px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.device-info {
padding: 8px;
margin-bottom: 15px;
background-color: #f5f7fa;
border-radius: 4px;
.info-item {
font-size: 12px;
line-height: 1.4;
.label {
color: #606266;
}
.value {
color: #303133;
font-weight: 500;
}
}
}
.playback-controls {
.el-slider {
margin-bottom: 15px;
}
.control-buttons {
display: flex;
justify-content: center;
gap: 10px;
.el-button {
padding: 7px 12px;
}
}
}
}
}
</style>
<style lang="scss">

View File

@ -0,0 +1,171 @@
<template>
<el-card class="playback-panel" header="轨迹控制台">
<el-row class="device-info">
<el-col :span="24" class="info-item">
<span class="label">时间</span>
<span class="value">{{ currentLog ? currentLog.at : '-' }}</span>
</el-col>
<el-col :span="24" class="info-item">
<span class="label">位置</span>
<span class="value">{{ currentLog ? `${currentLog.longitude}, ${currentLog.latitude}` : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">速度</span>
<span class="value">{{ currentSpeed }} Km/h</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">电压</span>
<span class="value">{{ currentLog ? currentLog.voltage + 'V' : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">信号</span>
<span class="value">{{ currentLog ? currentLog.signal : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">卫星</span>
<span class="value">{{ currentLog ? currentLog.satellites : '-' }}</span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">状态</span>
<span class="value"><dict-tag :options="dict.type.device_status" :value="currentLog.status" size="mini"/></span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">锁状态</span>
<span class="value"><dict-tag :options="dict.type.device_lock_status" :value="currentLog.lockStatus" size="mini"/></span>
</el-col>
<el-col :span="12" class="info-item">
<span class="label">电门</span>
<span class="value"><dict-tag :options="dict.type.device_quality" :value="currentLog.quality" size="mini"/></span>
</el-col>
</el-row>
<div class="playback-controls">
<el-slider
v-model="currentIndex"
:max="maxIndex"
:show-tooltip="false"
@input="handleSliderChange">
</el-slider>
<div class="control-buttons">
<el-button-group>
<el-button size="mini" :icon="isPlaying ? 'el-icon-video-pause' : 'el-icon-video-play'" @click="togglePlay">
{{ isPlaying ? '暂停' : '播放' }}
</el-button>
<el-button
v-for="speed in [1, 2, 4, 8]"
:key="speed"
size="mini"
:type="playbackSpeed === speed ? 'primary' : ''"
@click="setPlaybackSpeed(speed)">
{{ speed }}x
</el-button>
</el-button-group>
</div>
</div>
</el-card>
</template>
<script>
export default {
name: 'PlaybackPanel',
dicts: ['device_status', 'device_lock_status', 'device_quality'],
props: {
currentLog: {
type: Object,
default: () => ({})
},
currentSpeed: {
type: Number,
default: 0
},
maxIndex: {
type: Number,
default: 0
},
isPlaying: {
type: Boolean,
default: false
},
playbackSpeed: {
type: Number,
default: 1
}
},
data() {
return {
currentIndex: 0
}
},
watch: {
currentLog(newVal) {
//
if (newVal && newVal.index !== undefined) {
this.currentIndex = newVal.index;
}
}
},
methods: {
handleSliderChange(index) {
this.$emit('slider-change', index);
},
togglePlay() {
this.$emit('toggle-play');
},
setPlaybackSpeed(speed) {
this.$emit('speed-change', speed);
}
}
}
</script>
<style lang="scss" scoped>
.playback-panel {
position: absolute;
top: 60px;
right: 15px;
transform: none;
z-index: 100;
width: 400px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
.device-info {
padding: 8px;
margin-bottom: 15px;
background-color: #f5f7fa;
border-radius: 4px;
.info-item {
font-size: 12px;
line-height: 1.4;
.label {
color: #606266;
}
.value {
color: #303133;
font-weight: 500;
}
}
}
.playback-controls {
.el-slider {
margin-bottom: 15px;
}
.control-buttons {
display: flex;
justify-content: center;
gap: 10px;
.el-button {
padding: 7px 12px;
}
}
}
}
</style>

View File

@ -421,7 +421,7 @@ export default {
//
columns: [
{key: 'id', visible: false, label: 'ID', minWidth: null, sortable: true, overflow: false, align: 'center', width: "80"},
{key: 'sn', visible: true, label: '设备', minWidth: null, sortable: true, overflow: false, align: 'left', width: "180"},
{key: 'sn', visible: true, label: '设备', minWidth: null, sortable: true, overflow: false, align: 'left', width: "220"},
{key: 'vehicleNum', visible: true, label: '车辆', minWidth: null, sortable: true, overflow: false, align: 'left', width: null},
{key: 'mchName', visible: true, label: '归属', minWidth: null, sortable: true, overflow: false, align: 'left', width: null},
{key: 'signalStrength', visible: true, label: '信号', minWidth: null, sortable: true, overflow: false, align: 'center', width: null},

View File

@ -94,18 +94,20 @@ export default {
methods: {
//
getArea() {
if (!this.areaId) {
if (this.areaId == null) {
this.area = {};
this.areaSubList = [];
return;
}
getArea(this.areaId).then(res => {
this.area = res.data;
this.getAreaSubList();
});
},
//
getAreaSubList() {
if (this.areaId == null) {
this.areaSubList = [];
return;
}
listAreaSubByAreaId({ areaId: this.areaId }).then(res => {
this.areaSubList = res.data;
});