diff --git a/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss b/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss
index 1732e126..bb60ee17 100644
--- a/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss
+++ b/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss
@@ -23,9 +23,47 @@
.subscriber-map {
height: 100%;
width: 100%;
+
+ .maplibregl-canvas:focus-visible {
+ outline: none;
+ }
}
.panel-chart__no-data {
height: calc(100% - 46px);
}
}
+
+ .subscriber-map-point-tooltip {
+ position: fixed;
+ background-color: white;
+ width: 200px;
+ border: 1px solid #C5C5C5;
+ box-shadow: -1px 1px 10px -1px rgba(205,205,205,0.85);
+ border-radius: 2px;
+
+ .subscriber-map-point-tooltip__time {
+ padding: 10px;
+ font-size: 12px;
+ color: #353636;
+ font-weight: bold;
+ }
+ .subscriber-map-point-tooltip__coordinates {
+ padding: 0 10px 10px;
+
+ .subscriber-map-point-tooltip__coordinate {
+ display: flex;
+
+ .coordinate__label {
+ width: 115px;
+ font-size: 12px;
+ color: #575757;
+ }
+ .coordinate__value {
+ font-size: 12px;
+ color: #353636;
+ font-weight: bold;
+ }
+ }
+ }
+ }
}
diff --git a/src/utils/constants.js b/src/utils/constants.js
index aef62996..98b7748f 100644
--- a/src/utils/constants.js
+++ b/src/utils/constants.js
@@ -98,7 +98,7 @@ export const entityType = {
app: 'app',
domain: 'domain',
ip: 'ip',
- subscribe: 'subscribe'
+ subscriber: 'subscriber'
}
export const knowledgeCardUpdateRecordType = {
@@ -153,7 +153,7 @@ export const entityDetailTabConfig = [
]
},
{
- name: 'subscribe',
+ name: 'subscriber',
config: [
{ name: entityDetailTabsName.deviceInformation, label: 'entities.deviceInformation', icon: 'cn-icon cn-icon-device-info', tag: 0 },
{ name: entityDetailTabsName.accountInformation, label: 'entities.accountInformation', icon: 'cn-icon cn-icon-account-info', tag: 0 },
diff --git a/src/views/charts2/charts/entityDetail/EntityDetailMap.vue b/src/views/charts2/charts/entityDetail/EntityDetailMap.vue
index 019a3bc1..8d78bc21 100644
--- a/src/views/charts2/charts/entityDetail/EntityDetailMap.vue
+++ b/src/views/charts2/charts/entityDetail/EntityDetailMap.vue
@@ -17,6 +17,21 @@
+
+
+
@@ -28,9 +43,10 @@ import maplibregl from 'maplibre-gl'
import mapStyle from './mapStyle'
import axios from 'axios'
import { api } from '@/utils/api'
+import 'maplibre-gl/dist/maplibre-gl.css'
import _ from 'lodash'
import { ref } from 'vue'
-import { getNowTime } from '@/utils/date-util'
+import { getNowTime, getSecond } from '@/utils/date-util'
export default {
name: 'EntityDetailMap',
@@ -39,22 +55,19 @@ export default {
ChartError,
ChartNoData
},
- data () {
- return {
- }
- },
mounted () {
this.initMap()
},
methods: {
- initMap () {
+ async initMap () {
+ const _this = this
const map = new maplibregl.Map({
container: 'subscriberMap',
style: mapStyle,
- center: [116.39, 39.9],
- zoom: 9,
- maxZoom: 12,
- minZoom: 4,
+ center: this.center,
+ maxZoom: this.maxZoom,
+ minZoom: this.minZoom,
+ zoom: 7,
transformRequest: function (url, resourceType) {
if (resourceType === 'Tile' && url.indexOf('https://api.maptiler.com/tiles/v3') > -1) {
const urlParams = url.split('.pbf')[0].split('/')
@@ -85,52 +98,102 @@ export default {
}
})
- /* axios.get(`${api.entity.locationTrack}?resource=${this.entity.entityName}`).then(res => {
-
- }) */
- this.toggleLoading(false)
- const res = {
- status: 200,
- data: {
- data: {
- result: [
- {
- stat_time: 1701259999,
- subscriber_id: '883849',
- subscriber_longitude: 116.29,
- subscriber_latitude: 39.8
- },
- {
- stat_time: 1701269999,
- subscriber_id: '883849',
- subscriber_longitude: 116.34,
- subscriber_latitude: 39.85
- },
- {
- stat_time: 1701279999,
- subscriber_id: '883849',
- subscriber_longitude: 116.29,
- subscriber_latitude: 39.9
- },
- {
- stat_time: 1701299999,
- subscriber_id: '883849',
- subscriber_longitude: 116.39,
- subscriber_latitude: 39.9
- },
- {
- stat_time: 1701289999,
- subscriber_id: '883849',
- subscriber_longitude: 116.3,
- subscriber_latitude: 39.97
+ this.queryData().then(res => {
+ this.showError = false
+ const result = _.get(res, 'data.data.result', []).sort((a, b) => a.stat_time - b.stat_time)
+ const route = this.generateRouteGEOJSON(result)
+ const points = this.generatePointsGEOJSON(result)
+ map.on('load', () => {
+ map.jumpTo({ center: this.computeMapCenter(result) })
+ map.zoomTo(this.computeMapZoom(result))
+ map.loadImage(
+ `${window.location.protocol}//${window.location.host}/images/entity-detail/track-point.png`,
+ (error, image) => {
+ if (error) throw error
+ map.addImage('trace-point', image)
+ map.addSource('points', {
+ type: 'geojson',
+ data: points
+ })
+ map.addLayer(this.generatePointsLayer())
}
- ]
- }
- }
- }
- const result = _.get(res, 'data.data.result', []).sort((a, b) => a.stat_time - b.stat_time)
+ )
+ map.addSource('route', {
+ type: 'geojson',
+ lineMetrics: true,
+ data: route
+ })
+ map.addLayer(this.generateRouteLayer())
- const route = {
+ this.myChart = map
+
+ // point的鼠标事件
+ map.on('mouseenter', 'points', () => {
+ _this.tooltip.showTooltip = true
+ })
+ map.on('mouseleave', 'points', () => {
+ _this.tooltip.showTooltip = false
+ })
+ map.on('mousemove', 'points', ({ point, originalEvent, features }) => {
+ _this.tooltip.x = originalEvent.clientX + 10
+ _this.tooltip.y = originalEvent.clientY - 95
+ _this.currentPoint = { ...features[0].properties }
+ })
+ })
+ }).catch(e => {
+ this.showError = true
+ console.error(e)
+ this.errorMsg = this.errorMsgHandler(e)
+ }).finally(() => {
+ this.toggleLoading(false)
+ })
+ },
+ queryData () {
+ return new Promise((resolve, reject) => {
+ resolve({
+ status: 200,
+ data: {
+ data: {
+ result: [
+ {
+ stat_time: 1701259999,
+ subscriber_id: '883849',
+ subscriber_longitude: 116.29,
+ subscriber_latitude: 39.8
+ },
+ {
+ stat_time: 1701269999,
+ subscriber_id: '883849',
+ subscriber_longitude: 116.34,
+ subscriber_latitude: 39.85
+ },
+ {
+ stat_time: 1701279999,
+ subscriber_id: '883849',
+ subscriber_longitude: 116.29,
+ subscriber_latitude: 39.9
+ },
+ {
+ stat_time: 1701299999,
+ subscriber_id: '883849',
+ subscriber_longitude: 116.39,
+ subscriber_latitude: 39.9
+ },
+ {
+ stat_time: 1701289999,
+ subscriber_id: '883849',
+ subscriber_longitude: 116.3,
+ subscriber_latitude: 39.97
+ }
+ ]
+ }
+ }
+ })
+ })
+ // return axios.get(`${api.entity.locationTrack}?resource=${this.entity.entityName}`)
+ },
+ generateRouteGEOJSON (result) {
+ return {
type: 'FeatureCollection',
features: [
{
@@ -142,8 +205,9 @@ export default {
}
]
}
-
- const points = {
+ },
+ generatePointsGEOJSON (result) {
+ return {
type: 'FeatureCollection',
features: result.map((r, index) => {
return {
@@ -154,68 +218,91 @@ export default {
},
properties: {
index: index + 1,
- stat_time: r.stat_time
+ stat_time: r.stat_time,
+ longitude: r.subscriber_longitude,
+ latitude: r.subscriber_latitude
}
}
})
}
-
- map.on('load', () => {
- map.loadImage(
- `${window.location.protocol}//${window.location.host}/images/entity-detail/track-point.png`,
- (error, image) => {
- if (error) throw error
- map.addImage('trace-point', image)
- map.addSource('points', {
- type: 'geojson',
- data: points
- })
- map.addLayer({
- id: 'points',
- type: 'symbol',
- source: 'points',
- layout: {
- 'icon-image': 'trace-point',
- 'icon-size': 0.7,
- 'icon-offset': [0, -20],
- 'text-field': ['get', 'index'],
- 'text-font': ['Noto Sans Bold'],
- 'text-offset': [0, -1.1],
- 'text-size': 13
- },
- paint: {
- 'text-color': '#FFF'
- }
- })
- }
- )
- map.addSource('route', {
- type: 'geojson',
- lineMetrics: true,
- data: route
- })
- map.addLayer({
- id: 'route',
- source: 'route',
- type: 'line',
- paint: {
- 'line-color': '#E26154',
- 'line-width': 4,
- 'line-gradient': [
- 'interpolate',
- ['linear'],
- ['line-progress'],
- 0,
- '#F4B32F',
- 1,
- '#E26154'
- ]
- },
- layout: {
- 'line-cap': 'round',
- 'line-join': 'round'
- }
- })
+ },
+ generateRouteLayer () {
+ return {
+ id: 'route',
+ source: 'route',
+ type: 'line',
+ paint: {
+ 'line-color': '#E26154',
+ 'line-width': 4,
+ 'line-gradient': [
+ 'interpolate',
+ ['linear'],
+ ['line-progress'],
+ 0,
+ '#F4B32F',
+ 1,
+ '#E26154'
+ ]
+ },
+ layout: {
+ 'line-cap': 'round',
+ 'line-join': 'round'
+ }
+ }
+ },
+ generatePointsLayer () {
+ return {
+ id: 'points',
+ type: 'symbol',
+ source: 'points',
+ layout: {
+ 'icon-image': 'trace-point',
+ 'icon-size': 0.7,
+ 'icon-offset': [0, -20],
+ 'text-field': ['get', 'index'],
+ 'text-font': ['Noto Sans Bold'],
+ 'text-offset': [0, -1.1],
+ 'text-size': 13
+ },
+ paint: {
+ 'text-color': '#FFF'
+ }
+ }
+ },
+ computeMapCenter (result) {
+ const longitude = result.map(r => r.subscriber_longitude)
+ const latitude = result.map(r => r.subscriber_latitude)
+ return [
+ _.floor((_.max(longitude) + _.min(longitude)) / 2, 6),
+ _.floor((_.max(latitude) + _.min(latitude)) / 2, 6)
+ ]
+ },
+ computeMapZoom (result) {
+ const longitude = result.map(r => r.subscriber_longitude)
+ const latitude = result.map(r => r.subscriber_latitude)
+ const longitudeRange = _.max(longitude) - _.min(longitude)
+ const latitudeRange = _.max(latitude) - _.min(latitude)
+ const max = _.max([this.minZoom, 7 - Math.log2(_.max([longitudeRange, latitudeRange]))])
+ return _.min([max, this.maxZoom])
+ },
+ reload (startTime, endTime, dateRangeValue) {
+ this.toggleLoading(true)
+ this.timeFilter = { startTime: getSecond(startTime), endTime: getSecond(endTime), dateRangeValue: dateRangeValue }
+ this.queryData().then(res => {
+ this.showError = false
+ const result = _.get(res, 'data.data.result', []).sort((a, b) => a.stat_time - b.stat_time)
+ const route = this.generateRouteGEOJSON(result)
+ const points = this.generatePointsGEOJSON(result)
+ this.myChart.getSource('route').setData(route)
+ this.myChart.getSource('points').setData(points)
+ this.myChart.jumpTo({ center: this.computeMapCenter(result) })
+ this.myChart.zoomTo(this.computeMapZoom(result))
+ }).catch(e => {
+ this.showError = true
+ console.error(e)
+ this.errorMsg = this.errorMsgHandler(e)
+ }).finally(() => {
+ this.toggleLoading(false)
})
}
},
@@ -225,8 +312,19 @@ export default {
const { startTime, endTime } = getNowTime(dateRangeValue)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
+
+ const currentPoint = ref({})
+ const tooltip = ref({
+ showTooltip: false
+ })
+
return {
- timeFilter
+ timeFilter,
+ currentPoint,
+ tooltip,
+ maxZoom: 11,
+ minZoom: 1,
+ center: [116.38, 39.9]
}
}
}
diff --git a/src/views/entityExplorer/EntityDetail.vue b/src/views/entityExplorer/EntityDetail.vue
index 7079f5ac..766330ba 100644
--- a/src/views/entityExplorer/EntityDetail.vue
+++ b/src/views/entityExplorer/EntityDetail.vue
@@ -39,7 +39,7 @@ export default {
entityData.appName = query.entityName
break
}
- case 'subscribe': {
+ case 'subscriber': {
panelType = panelTypeAndRouteMapping.subscribeEntityDetail
entityData.appName = query.entityName
break