<template> <app-container id="overview"> <!--筛选条件--> <div class="search-div"> <el-form ref="selectForm" :inline="true" :model="listQuery" class="form-container"> <el-row > <el-col style="width: fit-content;"> <el-date-picker v-model="dateTimeRange" type="datetimerange" range-separator="至" size="small" value-format="yyyy-MM-dd HH:mm:ss" start-placeholder="开始时间" end-placeholder="结束时间" /> <!--</el-form-item>--> <el-button class="filter-item" type="primary" icon="el-icon-search" size="small" @click="search">查询</el-button> <el-button class="filter-item" type="primary" size="small" @click="play">开始</el-button> <el-button class="filter-item" type="primary" size="small" @click="pause">暂停</el-button> <el-button class="filter-item" type="primary" size="small" @click="stop">停止</el-button> </el-col> <el-col style="display:flex;width: fit-content;"> <span style="padding-left: 10px;padding-top: 10px;font-size: 14px">回放速度</span> <span style="padding-left:10px;padding-right: 10px;padding-top: 10px;font-size: 14px">快</span> <el-slider v-model="sec" :format-tooltip="formatTooltip" :step="10" :min="10" :show-tooltip="false" style="width: 100px;" @change="settimer"/> <span style="padding-left: 10px;padding-top: 10px;font-size: 14px;">慢</span> </el-col> <el-col style="padding-top: 4px;width: fit-content; float: right;"> <el-button class="filter-item" type="primary" style="margin-left: 10px;background-color: #286090;border: #286090" size="small" @click="staticspeed">时间速度曲线</el-button> </el-col> </el-row> <el-row> <el-slider v-model="showindex" :format-tooltip="timeTooltip" :max="list.length!==0?list.length-1:0" :disabled="list.length==0" class="timeslide" @change="changepos"/> </el-row> </el-form> </div> <div id="map"> <div class="namecard" >{{ carName }}</div> </div> <trail-static v-show="dialogFormVisible" ref="trailStatic" @watchChild="fetchData"/> </app-container> </template> <script> import trailStatic from '@/views/overview/trailStatic' import { trail } from '@/api/overview' import L from 'leaflet' import 'leaflet/dist/leaflet.css' import 'leaflet-rotatedmarker/leaflet.rotatedMarker.js' export default { name: 'Trail', components: { trailStatic }, data() { return { dialogFormVisible: false, // 是否显示编辑框 refresh: '10', sec: 50, showindex: 0, state: 0, // 1:开始 2:暂停 3:停止 showtable: true, deptShow: true, listQuery: { carId: '', startTime: '', endTime: '' }, // 查询条件 baselayer: [], carName: '', dateTimeRange: [], pathlist: [], // 列表数据 list: [], // 列表数据 map: null, listLoading: true, // 加载动画 fullscreenLoading: false, // 全屏加载动画 timer: null, carpoint: null } }, watch: { dateTimeRange(val) { if (val && val.length > 0) { this.listQuery.startTime = val[0] this.listQuery.endTime = val[1] } else { this.listQuery.startTime = '' this.listQuery.endTime = '' } } }, mounted() { if (this.$route.query) { this.listQuery.carId = this.$route.query.id this.carName = this.$route.query.name } const date = new Date() const year = date.getFullYear() // 年 const month = date.getMonth() + 1 // 月 const day = date.getDate() // 日 this.dateTimeRange = [year + '-' + month + '-' + day + ' 00:00:00', year + '-' + month + '-' + day + ' 23:59:59'] this.initMap() this.settimer() }, methods: { settimer() { clearTimeout(this.timer) this.timer = setInterval(() => { this.showcar() }, this.sec * 10) }, staticspeed() { this.dialogFormVisible = true this.$refs.trailStatic.initDialog(this.dialogFormVisible, this.listQuery, this.carName) }, showcar() { if (this.state !== 1) { return } this.showindex++ if (this.showindex === this.list.length) { clearTimeout(this.timer) var that = this setTimeout(function() { that.stop() }, this.sec * 10) return } this.carpoint.remove() // add point var Icon = L.icon({ iconUrl: require('../../assets/global_images/car.png'), iconSize: [40, 40] }) var str = '<div style="font-size: 15px;padding: 6px"> <div style="font-weight: bold;padding-bottom: 10px">车辆轨迹点信息</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">定位</label>' + this.list[this.showindex].lng + ' , ' + this.list[this.showindex].lat + '</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">时间</label>' + this.list[this.showindex].upTime + '</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">速度</label>' + this.list[this.showindex].speed + '(km/h)</div>' + '</div>' var popup = L.popup().setContent(str) this.carpoint = L.marker([this.list[this.showindex].lat, this.list[this.showindex].lng], { icon: Icon, rotationAngle: this.list[this.showindex].direction }).addTo(this.map).bindPopup(popup) this.carpoint.openPopup() }, play() { this.state = 1 this.settimer() }, pause() { this.state = 2 }, changepos() { if (this.list.length !== 0) { if (this.carpoint !== null) { this.carpoint.remove() } var Icon = L.icon({ iconUrl: require('../../assets/global_images/car.png'), iconSize: [40, 40] }) var str = '<div style="font-size: 15px;padding: 6px"> <div style="font-weight: bold;padding-bottom: 10px">车辆轨迹点信息</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">定位</label>' + this.list[this.showindex].lng + ' , ' + this.list[this.showindex].lat + '</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">时间</label>' + this.list[this.showindex].upTime + '</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">速度</label>' + this.list[this.showindex].speed + '(km/h)</div>' + '</div>' var popup = L.popup().setContent(str) this.carpoint = L.marker([this.list[this.showindex].lat, this.list[this.showindex].lng], { icon: Icon, rotationAngle: Number(this.list[this.showindex].direction) }).addTo(this.map).bindPopup(popup) this.carpoint.openPopup() } }, stop() { this.state = 3 if (this.list.length !== 0) { if (this.carpoint !== null) { this.carpoint.remove() } var Icon = L.icon({ iconUrl: require('../../assets/global_images/car.png'), iconSize: [40, 40] }) var str = '<div style="font-size: 15px;padding: 6px"> <div style="font-weight: bold;padding-bottom: 10px">车辆轨迹点信息</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">定位</label>' + this.list[0].lng + ' , ' + this.list[0].lat + '</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">时间</label>' + this.list[0].upTime + '</div>' + '<div style="padding-bottom: 6px"><label style="font-weight: bold;padding-right: 15px">速度</label>' + this.list[0].speed + '(km/h)</div>' + '</div>' var popup = L.popup().setContent(str) this.carpoint = L.marker([this.list[0].lat, this.list[0].lng], { icon: Icon, rotationAngle: Number(this.list[0].direction) }).addTo(this.map).bindPopup(popup) this.carpoint.openPopup() this.showindex = 0 } }, formatTooltip(val) { return val / 100 }, timeTooltip(val) { if (this.list.length !== 0) { return this.list[val].upTime } }, initMap() { const map = L.map('map', { minZoom: 3, maxZoom: 18, center: [27.75962, 116.06021], zoom: 11, zoomControl: false, attributionControl: false, crs: L.CRS.EPSG3857 }) this.map = map // data上需要挂载 window.map = map this.baselayer.push(L.tileLayer( 'https://t0.tianditu.gov.cn/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8', { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] } ).addTo(map)) this.baselayer.push(L.tileLayer( 'https://t0.tianditu.gov.cn/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cva&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk=216ee92889e17ab1b083fae665d522b8', { subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'] } ).addTo(map)) this.search() }, search() { this.fetchData(false) }, setZoom(points) { console.log(points) if (points.length > 0) { var maxLng = points[0][1] var minLng = points[0][1] var maxLat = points[0][0] var minLat = points[0][0] var res for (var i = points.length - 1; i >= 0; i--) { res = points[i] if (res[1] > maxLng) maxLng = res[1] if (res[1] < minLng) minLng = res[1] if (res[0] > maxLat) maxLat = res[0] if (res[0] < minLat) minLat = res[0] } var cenLng = (parseFloat(maxLng) + parseFloat(minLng)) / 2 var cenLat = (parseFloat(maxLat) + parseFloat(minLat)) / 2 var zoom = this.getZoom(maxLng, minLng, maxLat, minLat) this.map.setView({ lat: cenLat, lng: cenLng }, zoom) } else { // 没有坐标,显示全中国 this.map.setView({ lat: 103.388611, lng: 35.563611 }, 5) } }, getZoom(maxLng, minLng, maxLat, minLat) { var zoom = ['50', '100', '200', '500', '1000', '2000', '5000', '10000', '20000', '25000', '50000', '100000', '200000', '500000', '1000000', '2000000']// 级别18到3。 var latlng = L.latLng(maxLat, maxLng) var distance = latlng.distanceTo(L.latLng(minLat, minLng)) // var distance = pointA.distanceTo(pointB)// 获取两点距离,保留小数点后两位 for (var i = 0, zoomLen = zoom.length; i < zoomLen; i++) { if (zoom[i] - distance > 0) { console.log(i) return 18 - i + 2 // 之所以会多2,是因为地图范围常常是比例尺距离的10倍以上。所以级别会增加3。 } } }, setCenter(points) { // 获取折线中心点坐标 if (points.length > 0) { let maxLng = points[0][1] let minLng = points[0][1] let maxLat = points[0][0] let minLat = points[0][0] let res for (let i = points.length - 1; i >= 0; i--) { res = points[i] if (res[1] > maxLng) maxLng = res[1] if (res[1] < minLng) minLng = res[1] if (res[0] > maxLat) maxLat = res[0] if (res[0] < minLat) minLat = res[0] } const cenLng = (parseFloat(maxLng) + parseFloat(minLng)) / 2 const cenLat = (parseFloat(maxLat) + parseFloat(minLat)) / 2 this.map.setView({ lat: cenLat, lng: cenLng }) } }, fetchData() { // 划线 this.listLoading = true trail(this.listQuery).then(response => { this.list = response.data var base = this.baselayer this.map.eachLayer(function(layer) { if (layer !== base[0] && layer !== base[1]) { layer.remove() } }) if (this.list.length === 0) { this.$message.warning('查询结果为空') } if (this.list.length !== 0) { var myStyle = { 'color': '#e69010', 'weight': 5 } this.pathlist = [] for (var i = 0; i < this.list.length; i++) { var item = [Number(this.list[i].lat), Number(this.list[i].lng)] this.pathlist.push(item) } L.polyline(this.pathlist, myStyle).addTo(this.map) this.setZoom(this.pathlist) var Icon = L.icon({ iconUrl: require('../../assets/global_images/start.png'), iconSize: [40, 40] }) L.marker([this.list[0].lat, this.list[0].lng], { icon: Icon }).addTo(this.map) Icon = L.icon({ iconUrl: require('../../assets/global_images/end.png'), iconSize: [40, 40] }) L.marker([this.list[this.list.length - 1].lat, this.list[this.list.length - 1].lng], { icon: Icon }).addTo(this.map) } this.stop() }) } } } </script> <style rel="stylesheet/scss" lang="scss" scoped> .search-div{ padding:12px; padding-bottom: 2px; /* 本身输入框有10px下边距*/ background-color: #fff; /*display: -webkit-flex; !* Safari *!*/ /*display: flex;*/ justify-content:space-between; .search-btn{ margin-bottom:10px; } } .namecard{ width: 300px; height: 20px; z-index: 10000; position: absolute; left:10px; top:10px; font-size: 16px; font-weight: bold; color: #1f2d3d; } #map { border-top: 12px solid #ebebeb; width:100%; height:67.5vh; } .my-div-icon { width: 10px; height: 10px; background-color: orange; border-radius: 50%; } .timeslide{ padding-left: 20px;padding-right: 20px; /deep/ .el-slider__button{ border-radius: 20%; width: 40px; height: 20px; } /deep/ .el-slider__button-wrapper{ width: 40px; } } </style>