更新
This commit is contained in:
parent
0207c44b44
commit
a382207c64
|
@ -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
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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">
|
||||
|
|
171
src/views/bst/areaSub/components/PlaybackPanel.vue
Normal file
171
src/views/bst/areaSub/components/PlaybackPanel.vue
Normal 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>
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue
Block a user