summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author刘洪洪 <[email protected]>2024-03-13 18:00:39 +0800
committer刘洪洪 <[email protected]>2024-03-13 18:00:39 +0800
commitf4289fb29d613d3db673dd4633072408518393b5 (patch)
treebf4e4a10635514849b3680d2eebb24fd48a4b358
parentfef0afbb4766564340b34a3e287b028cd4de6e85 (diff)
CN-1577 fix: Subscriber实体详情地图内容更换为新版
-rw-r--r--src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss95
-rw-r--r--src/views/charts2/charts/entityDetail/EntityDetailMap.vue739
2 files changed, 741 insertions, 93 deletions
diff --git a/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss b/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss
index 52737329..1cbe3e28 100644
--- a/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss
+++ b/src/assets/css/components/views/charts2/entityDetailSubscriberMap.scss
@@ -266,4 +266,99 @@
}
}
}
+ .geo-analysis__hexagon-tooltip {
+ position: fixed;
+ background-color: rgba(255,255,255,0.80);
+ box-shadow: 0 1px 10px 0 rgba(0,0,0,0.5);
+ border-radius: 2px;
+ min-width: 185px;
+ z-index: 3;
+
+ /*&.geo-analysis__hexagon-tooltip--hexagon {
+ }*/
+ &.geo-analysis__hexagon-tooltip--human {
+ .icon__box {
+ background-color: #233447;
+ }
+ }
+ &.geo-analysis__hexagon-tooltip--base-station {
+ .icon__box {
+ background-color: #585B5F;
+ }
+ .hexagon-tooltip__body {
+ .body__item .item__label {
+ width: 140px;
+ }
+ }
+ }
+ .hexagon-tooltip__header {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ padding: 10px 0 10px 63px;
+ color: white;
+
+ .header__icon {
+ position: absolute;
+ left: 14px;
+ top: 16px;
+
+ .icon__box {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 32px;
+ width: 32px;
+ border-radius: 50%;
+ }
+ }
+ .header__title {
+ font-size: 16px;
+ }
+ .header__content {
+ font-size: 14px;
+ }
+ }
+ .hexagon-tooltip__body {
+ padding: 8px 18px;
+
+ .body__timeline {
+ display: flex;
+ flex-direction: row;
+ padding: 3px 0;
+
+ .timeline-symbol {
+ margin-top: 6px;
+ margin-right: 6px;
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: #DE3434;
+ }
+ }
+ .body__item {
+ display: flex;
+
+ .item__label {
+ padding-right: 10px;
+ text-align: right;
+ width: 60px;
+ font-size: 12px;
+ color: #353636;
+ }
+ .item__value {
+ font-size: 12px;
+ font-weight: bold;
+ color: #233447;
+ }
+ }
+ .body__tracking {
+ padding-top: 6px;
+ font-size: 12px;
+ color: #38ACD2;
+ text-decoration: underline;
+ cursor: pointer;
+ }
+ }
+ }
}
diff --git a/src/views/charts2/charts/entityDetail/EntityDetailMap.vue b/src/views/charts2/charts/entityDetail/EntityDetailMap.vue
index 4525ac07..d22c10f4 100644
--- a/src/views/charts2/charts/entityDetail/EntityDetailMap.vue
+++ b/src/views/charts2/charts/entityDetail/EntityDetailMap.vue
@@ -37,7 +37,10 @@
<div class="scroll-list" :style="`transform: translateY(${trackingSubscriber.startOffset}px)`">
<div class="scroll__item"
v-for="(record, index) in trackingSubscriber.trackRecords.slice(trackingSubscriber.scrollStartIndex, trackingSubscriber.scrollEndIndex)"
- :key="index">
+ :key="index"
+ @mouseenter="timelineMouseEnter(trackingSubscriber, record)"
+ @mouseleave="timelineMouseLeave(trackingSubscriber, record)"
+ >
<div class="item-circle">
<div class="circle-circle"></div>
<div class="circle-line"></div>
@@ -65,18 +68,123 @@
</div>
<!-- 地图point悬浮框 -->
- <div class="subscriber-map-point-tooltip" v-if="tooltip.showTooltip" :style="{'left': `${tooltip.x}px`, 'top': `${tooltip.y}px`}">
- <div class="subscriber-map-point-tooltip__time">{{dateFormatByAppearance(currentPoint.stat_time)}}</div>
- <div class="subscriber-map-point-tooltip__coordinates">
- <div class="subscriber-map-point-tooltip__coordinate">
- <div class="coordinate__label">{{$t('overall.longitude')}}</div>
- <div class="coordinate__value">{{currentPoint.longitude}}</div>
+<!-- <div class="subscriber-map-point-tooltip" v-if="tooltip.showTooltip" :style="{'left': `${tooltip.x}px`, 'top': `${tooltip.y}px`}">-->
+<!-- <div class="subscriber-map-point-tooltip__time">{{dateFormatByAppearance(currentPoint.stat_time)}}</div>-->
+<!-- <div class="subscriber-map-point-tooltip__coordinates">-->
+<!-- <div class="subscriber-map-point-tooltip__coordinate">-->
+<!-- <div class="coordinate__label">{{$t('overall.longitude')}}</div>-->
+<!-- <div class="coordinate__value">{{currentPoint.longitude}}</div>-->
+<!-- </div>-->
+<!-- <div class="subscriber-map-point-tooltip__coordinate">-->
+<!-- <div class="coordinate__label">{{$t('overall.latitude')}}</div>-->
+<!-- <div class="coordinate__value">{{currentPoint.latitude}}</div>-->
+<!-- </div>-->
+<!-- </div>-->
+<!-- </div>-->
+
+ <div class="geo-analysis__hexagon-tooltip" id="tooltip" :class="`geo-analysis__hexagon-tooltip--${tooltip.type}`" v-if="tooltip.showMarkerTooltip || tooltip.showPolygonTooltip" :style="{'left': `${tooltip.x}px`, 'top': `${tooltip.y}px`}" @mouseenter="tooltipMouseEnter" @mouseleave="tooltipMouseLeave">
+ <div class="hexagon-tooltip__header" :style="`background-color: ${tooltipHeaderColor}`">
+ <div class="header__icon">
+ <svg v-if="tooltip.type === tooltipType.hexagon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="36" height="36"><path d="M747.52 921.088H277.504L42.496 514.048l235.008-407.04H747.52l235.008 407.04-235.008 407.04z m-425.472-76.8h381.44l190.464-330.24-190.464-330.24h-381.44l-190.464 330.24 190.464 330.24z"></path></svg>
+ <template v-else-if="tooltip.type === tooltipType.human">
+ <div class="icon__box">
+ <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M366.689524 690.468571l87.283809 83.821715-75.434666 195.486476c-10.971429 27.794286-43.105524 42.081524-72.265143 32.036571-29.159619-10.24-44.080762-41.252571-33.450667-69.241904L366.689524 690.468571zM203.824762 476.306286l51.785143-95.183238a162.279619 162.279619 0 0 1 59.245714-59.977143c119.710476-68.266667 134.777905-67.291429 149.942857-66.218667l80.798476 5.168762c24.868571 0.975238 42.081524 7.314286 125.025524 124.14781a21.26019 21.26019 0 0 0 14.092191 8.289523l99.132952 14.482286c12.873143 1.852952 24.478476 8.582095 32.182857 18.67581a45.494857 45.494857 0 0 1 8.825905 35.108571 46.665143 46.665143 0 0 1-19.504762 30.866286 50.468571 50.468571 0 0 1-36.571429 8.435809l-99.181714-14.433524a119.954286 119.954286 0 0 1-79.774476-47.640381c-4.388571-6.241524-7.558095-11.361524-11.849143-16.579047l-63.634286 193.487238 88.405334 84.845714c12.970667 12.385524 23.698286 29.013333 30.232381 45.494857l67.876571 190.366477c5.022476 13.409524 4.193524 28.233143-2.291809 41.057523a54.613333 54.613333 0 0 1-32.182858 27.160381c-5.851429 2.048-12.092952 3.120762-18.383238 3.169524a58.075429 58.075429 0 0 1-53.930666-36.181333l-67.876572-190.366476c-1.024-2.096762-2.096762-3.120762-3.218285-5.168762L365.616762 623.177143a84.894476 84.894476 0 0 1-24.868572-80.700953l34.523429-146.919619c-3.413333 2.340571-7.070476 4.388571-10.776381 6.290286a58.806857 58.806857 0 0 0-22.674286 22.723048L290.133333 519.801905a49.834667 49.834667 0 0 1-43.105523 24.819809 59.977143 59.977143 0 0 1-22.674286-5.12 46.518857 46.518857 0 0 1-20.48-63.146666z m209.67619-360.448C420.08381 58.465524 473.86819 17.066667 533.650286 23.30819 593.383619 29.549714 636.537905 81.13981 630.00381 138.48381c-6.534095 57.392762-60.269714 98.840381-120.05181 92.550095-59.782095-6.241524-102.985143-57.782857-96.451048-115.175619z"></path></svg>
+ </div>
+ </template>
+ <template v-else-if="tooltip.type === tooltipType.baseStation">
+ <div class="icon__box">
+ <svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" width="20" height="20"><path d="M164.901926 519.585185a391.35763 391.35763 0 0 1-30.151111-152.38637c0-52.527407 10.05037-105.054815 30.151111-152.348445 15.094519-47.29363 45.283556-89.353481 80.516741-126.103703L174.990222 15.17037C129.706667 62.464 94.511407 114.991407 69.328593 178.062222 44.183704 241.095111 34.133333 304.165926 34.133333 367.198815c0 63.070815 10.05037 131.375407 35.19526 189.174518 25.182815 57.799111 60.378074 115.598222 105.661629 162.891852l70.428445-73.576296c-35.233185-36.788148-65.422222-78.810074-80.516741-126.103704z" p-id="8786"></path><path d="M255.469037 477.563259c15.094519 36.788148 35.195259 68.266667 60.340148 94.549334l70.428445-73.576297a233.168593 233.168593 0 0 1-40.201482-57.761185c-5.044148-26.282667-10.088296-47.29363-10.088296-73.576296 0-26.244741 5.044148-47.29363 15.094518-68.266667 5.006222-26.282667 20.100741-42.097778 35.19526-63.070815l-70.428445-73.576296c-25.144889 26.282667-45.24563 57.799111-60.340148 94.587259-15.094519 36.788148-20.100741 73.576296-20.100741 110.364445 0 36.788148 5.006222 73.53837 20.100741 110.326518zM436.527407 367.198815c0 43.538963 33.792 78.810074 75.472593 78.810074s75.472593-35.271111 75.472593-78.810074c0-43.501037-33.792-78.810074-75.472593-78.810074s-75.472593 35.271111-75.472593 78.810074zM637.76237 498.574222l70.428445 73.576297c25.144889-26.282667 45.24563-57.837037 60.340148-94.58726 15.094519-36.788148 20.100741-73.576296 20.100741-110.364444 0-36.788148-5.006222-73.576296-20.100741-110.364445-15.094519-36.750222-35.195259-68.266667-60.340148-94.549333l-70.428445 73.576296c15.094519 15.739259 30.189037 36.788148 40.201482 57.799111 10.088296 21.010963 15.132444 47.29363 15.132444 68.266667 0 26.282667-5.044148 47.331556-15.094518 68.342519-10.05037 31.516444-25.144889 47.29363-40.239408 68.266666z" p-id="8787"></path><path d="M954.671407 178.062222C929.488593 114.991407 894.293333 62.464 849.009778 15.17037L778.619259 88.746667c35.233185 36.788148 60.378074 78.810074 80.516741 126.103703 20.100741 47.255704 30.151111 99.821037 30.151111 152.348445 0 52.565333-10.05037 105.092741-30.151111 152.38637-20.100741 47.29363-45.283556 89.315556-80.516741 126.103704l70.428445 73.576296c45.283556-47.29363 80.478815-99.858963 105.661629-162.891852 25.144889-63.070815 35.195259-126.103704 35.19526-189.174518 0-63.032889-10.05037-131.337481-35.19526-189.136593zM210.185481 1024h603.629038L512 551.10163 210.185481 1024z m186.102519-105.054815L512 740.238222l115.674074 178.631111h-231.348148z"></path></svg>
+ </div>
+ </template>
</div>
- <div class="subscriber-map-point-tooltip__coordinate">
- <div class="coordinate__label">{{$t('overall.latitude')}}</div>
- <div class="coordinate__value">{{currentPoint.latitude}}</div>
+ <div class="header__title">
+ <template v-if="tooltip.type === tooltipType.hexagon">HEX</template>
+ <template v-else-if="tooltip.type === tooltipType.human">MSISDN</template>
+ <template v-else-if="tooltip.type === tooltipType.baseStation">CID</template>
+ </div>
+ <div class="header__content">
+ <template v-if="tooltip.type === tooltipType.hexagon">{{currentPolygon.hexId}}</template>
+ <template v-else-if="tooltip.type === tooltipType.human">{{currentSubscriber.subscriberDto.phoneNumber}}</template>
+ <template v-else-if="tooltip.type === tooltipType.baseStation">0xxa8805</template>
</div>
</div>
+ <div class="hexagon-tooltip__body">
+ <template v-if="tooltip.type === tooltipType.hexagon">
+ <template v-if="activeTab === 'locationMap'">
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.number') }}</div>
+ <div class="item__value">{{currentPolygon.number}}</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.locals') }}</div>
+ <div class="item__value">{{currentPolygon.number}}</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.visitors') }}</div>
+ <div class="item__value">{{currentPolygon.number}}</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.roamers') }}</div>
+ <div class="item__value">{{currentPolygon.number}}</div>
+ </div>
+ </template>
+ <template v-else>
+ <template v-for="(item, i) in JSON.parse(currentPolygon.locations)" :key="item.hexId">
+ <div class="body__timeline" v-if="i < 5">
+ <div class="timeline-symbol"></div>
+ <div>
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.location') }}</div>
+ <div class="item__value">{{item.longitude}},&nbsp;{{item.latitude}}</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">Time</div>
+ <div class="item__value">{{dateFormatByAppearance(Number(item.time))}}</div>
+ </div>
+ </div>
+ </div>
+ </template>
+ <div class="body__timeline" v-if="JSON.parse(currentPolygon.locations).length > 5">...</div>
+ </template>
+ </template>
+ <template v-else-if="tooltip.type === tooltipType.human">
+ <div class="body__item">
+ <div class="item__label">ID</div>
+ <div class="item__value">{{currentSubscriber.subscriberId}}</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{$t('entities.group')}}</div>
+ <div class="item__value">Terrorist</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{$t('overall.info')}}</div>
+ <div class="item__value">Leader</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{$t('overall.location')}}</div>
+ <div class="item__value">China, Shanghai</div>
+ </div>
+ <div class="body__tracking" @click="trackSubscriber(currentSubscriber)">{{$t('location.traceTracking')}}</div>
+ </template>
+ <template v-else-if="tooltip.type === tooltipType.baseStation">
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.locationAreaCode') }}</div>
+ <div class="item__value">12</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.mobileNetworkCode') }}</div>
+ <div class="item__value">1</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{ $t('location.communicationType') }}</div>
+ <div class="item__value">4G</div>
+ </div>
+ <div class="body__item">
+ <div class="item__label">{{$t('overall.location')}}</div>
+ <div class="item__value">China, Shanghai</div>
+ </div>
+ </template>
+ </div>
</div>
</div>
</template>
@@ -94,7 +202,11 @@ import _ from 'lodash'
import { ref, shallowRef } from 'vue'
import { getNowTime, getSecond, dateFormatByAppearance } from '@/utils/date-util'
import unitConvert from '@/utils/unit-convert'
-import { unitTypes } from '@/utils/constants'
+import { defaultMapConfig, storageKey, unitTypes } from '@/utils/constants'
+import { h3ToGeo, h3ToGeoBoundary } from 'h3-js'
+import { overwriteUrl, urlParamsHandler } from '@/utils/tools'
+const humanSvg = '<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M366.689524 690.468571l87.283809 83.821715-75.434666 195.486476c-10.971429 27.794286-43.105524 42.081524-72.265143 32.036571-29.159619-10.24-44.080762-41.252571-33.450667-69.241904L366.689524 690.468571zM203.824762 476.306286l51.785143-95.183238a162.279619 162.279619 0 0 1 59.245714-59.977143c119.710476-68.266667 134.777905-67.291429 149.942857-66.218667l80.798476 5.168762c24.868571 0.975238 42.081524 7.314286 125.025524 124.14781a21.26019 21.26019 0 0 0 14.092191 8.289523l99.132952 14.482286c12.873143 1.852952 24.478476 8.582095 32.182857 18.67581a45.494857 45.494857 0 0 1 8.825905 35.108571 46.665143 46.665143 0 0 1-19.504762 30.866286 50.468571 50.468571 0 0 1-36.571429 8.435809l-99.181714-14.433524a119.954286 119.954286 0 0 1-79.774476-47.640381c-4.388571-6.241524-7.558095-11.361524-11.849143-16.579047l-63.634286 193.487238 88.405334 84.845714c12.970667 12.385524 23.698286 29.013333 30.232381 45.494857l67.876571 190.366477c5.022476 13.409524 4.193524 28.233143-2.291809 41.057523a54.613333 54.613333 0 0 1-32.182858 27.160381c-5.851429 2.048-12.092952 3.120762-18.383238 3.169524a58.075429 58.075429 0 0 1-53.930666-36.181333l-67.876572-190.366476c-1.024-2.096762-2.096762-3.120762-3.218285-5.168762L365.616762 623.177143a84.894476 84.894476 0 0 1-24.868572-80.700953l34.523429-146.919619c-3.413333 2.340571-7.070476 4.388571-10.776381 6.290286a58.806857 58.806857 0 0 0-22.674286 22.723048L290.133333 519.801905a49.834667 49.834667 0 0 1-43.105523 24.819809 59.977143 59.977143 0 0 1-22.674286-5.12 46.518857 46.518857 0 0 1-20.48-63.146666z m209.67619-360.448C420.08381 58.465524 473.86819 17.066667 533.650286 23.30819 593.383619 29.549714 636.537905 81.13981 630.00381 138.48381c-6.534095 57.392762-60.269714 98.840381-120.05181 92.550095-59.782095-6.241524-102.985143-57.782857-96.451048-115.175619z"></path></svg>'
+const baseStationSvg = '<svg viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M164.901926 519.585185a391.35763 391.35763 0 0 1-30.151111-152.38637c0-52.527407 10.05037-105.054815 30.151111-152.348445 15.094519-47.29363 45.283556-89.353481 80.516741-126.103703L174.990222 15.17037C129.706667 62.464 94.511407 114.991407 69.328593 178.062222 44.183704 241.095111 34.133333 304.165926 34.133333 367.198815c0 63.070815 10.05037 131.375407 35.19526 189.174518 25.182815 57.799111 60.378074 115.598222 105.661629 162.891852l70.428445-73.576296c-35.233185-36.788148-65.422222-78.810074-80.516741-126.103704z" p-id="8786"></path><path d="M255.469037 477.563259c15.094519 36.788148 35.195259 68.266667 60.340148 94.549334l70.428445-73.576297a233.168593 233.168593 0 0 1-40.201482-57.761185c-5.044148-26.282667-10.088296-47.29363-10.088296-73.576296 0-26.244741 5.044148-47.29363 15.094518-68.266667 5.006222-26.282667 20.100741-42.097778 35.19526-63.070815l-70.428445-73.576296c-25.144889 26.282667-45.24563 57.799111-60.340148 94.587259-15.094519 36.788148-20.100741 73.576296-20.100741 110.364445 0 36.788148 5.006222 73.53837 20.100741 110.326518zM436.527407 367.198815c0 43.538963 33.792 78.810074 75.472593 78.810074s75.472593-35.271111 75.472593-78.810074c0-43.501037-33.792-78.810074-75.472593-78.810074s-75.472593 35.271111-75.472593 78.810074zM637.76237 498.574222l70.428445 73.576297c25.144889-26.282667 45.24563-57.837037 60.340148-94.58726 15.094519-36.788148 20.100741-73.576296 20.100741-110.364444 0-36.788148-5.006222-73.576296-20.100741-110.364445-15.094519-36.750222-35.195259-68.266667-60.340148-94.549333l-70.428445 73.576296c15.094519 15.739259 30.189037 36.788148 40.201482 57.799111 10.088296 21.010963 15.132444 47.29363 15.132444 68.266667 0 26.282667-5.044148 47.331556-15.094518 68.342519-10.05037 31.516444-25.144889 47.29363-40.239408 68.266666z" p-id="8787"></path><path d="M954.671407 178.062222C929.488593 114.991407 894.293333 62.464 849.009778 15.17037L778.619259 88.746667c35.233185 36.788148 60.378074 78.810074 80.516741 126.103703 20.100741 47.255704 30.151111 99.821037 30.151111 152.348445 0 52.565333-10.05037 105.092741-30.151111 152.38637-20.100741 47.29363-45.283556 89.315556-80.516741 126.103704l70.428445 73.576296c45.283556-47.29363 80.478815-99.858963 105.661629-162.891852 25.144889-63.070815 35.195259-126.103704 35.19526-189.174518 0-63.032889-10.05037-131.337481-35.19526-189.136593zM210.185481 1024h603.629038L512 551.10163 210.185481 1024z m186.102519-105.054815L512 740.238222l115.674074 178.631111h-231.348148z"></path></svg>'
export default {
name: 'EntityDetailMap',
@@ -103,25 +215,74 @@ export default {
ChartError,
ChartNoData
},
+ data () {
+ return {
+ tooltipType: {
+ hexagon: 'hexagon',
+ baseStation: 'base-station',
+ human: 'human'
+ }
+ }
+ },
mounted () {
this.initMap()
- this.queryTraceTracking()
},
unmounted () {
- this.myChart && this.myChart?.remove?.()
- this.myChart = null
+ this.mapChart && this.mapChart?.remove?.()
+ this.mapChart = null
+ },
+ computed: {
+ tooltipHeaderColor () {
+ if (this.tooltip.type === this.tooltipType.hexagon) {
+ const color = this.currentPolygon.color.split(',')
+ color[0] = color[0].split('[')[1]
+ color[2] = color[2].split(']')[0]
+ return `rgba(${color.join(',')},.8)`
+ } else if (this.tooltip.type === this.tooltipType.human) {
+ return '#38ACD2'
+ } else if (this.tooltip.type === this.tooltipType.baseStation) {
+ return '#233447'
+ }
+ return ''
+ }
+ },
+ watch: {
+ // 切换追踪的用户
+ currentShowSubscriber (n) {
+ this.mapChart.getLayer('trackingHexagon') && this.mapChart.removeLayer('trackingHexagon')
+ this.mapChart.getLayer('trackingLine') && this.mapChart.removeLayer('trackingLine')
+ this.mapChart.getSource('trackingHexGrid') && this.mapChart.removeSource('trackingHexGrid')
+ this.mapChart.getSource('trackingLineSource') && this.mapChart.removeSource('trackingLineSource')
+ this.trackingHumanMarker.remove && this.trackingHumanMarker.remove()
+ this.trackingHumanMarker = {}
+ if (n) {
+ this.renderTrackingHexagon()
+ }
+ },
+ timeFilter (n) {
+ this.unbindTrackingHexagonEvents()
+ this.mapChart.getLayer('trackingHexagon') && this.mapChart.removeLayer('trackingHexagon')
+ this.mapChart.getLayer('trackingLine') && this.mapChart.removeLayer('trackingLine')
+ this.mapChart.getSource('trackingHexGrid') && this.mapChart.removeSource('trackingHexGrid')
+ this.mapChart.getSource('trackingLineSource') && this.mapChart.removeSource('trackingLineSource')
+ this.trackingHumanMarker.remove && this.trackingHumanMarker.remove()
+ this.trackingHumanMarker = {}
+ this.changeTimeFilterToInitList()
+ this.initTraceTrackingTab()
+ }
},
methods: {
dateFormatByAppearance,
async initMap () {
const _this = this
- const map = new maplibregl.Map({
+ this.toggleLoading(false)
+ this.mapChart = new maplibregl.Map({
container: 'subscriberMap',
style: mapStyle,
center: this.center,
maxZoom: this.maxZoom,
minZoom: this.minZoom,
- zoom: 7
+ zoom: this.defaultZoom
})
maplibregl.addProtocol('cn', (params, callback) => { // 切片显示接口 防止跨域的问题
fetch(`${params.url.split('://')[1]}`)
@@ -139,58 +300,60 @@ export default {
})
return { cancel: () => { } }
})
- this.queryData().then(res => {
- map.on('load', () => {
- 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)
- if (result.length > 0) {
- 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())
- }
- )
- map.addSource('route', {
- type: 'geojson',
- lineMetrics: true,
- data: route
- })
- map.addLayer(this.generateRouteLayer())
+ this.mapChart.on('load', async () => {
+ // 加载地图上的基站,基站不随tab的切换而改变
+ const baseStationData = await _this.queryBaseStation()
+ _this.renderMarker(baseStationData, _this.tooltipType.baseStation)
- 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)
+ _this.initTraceTrackingTab()
})
},
+ async initTraceTrackingTab () {
+ await this.queryTraceTracking()
+ if (!this.currentShowSubscriber && this.trackingSubscriber) {
+ this.currentShowSubscriber = this.trackingSubscriber
+ }
+ this.renderTrackingHexagon()
+ },
+ async queryBaseStation () {
+ // this.loading.baseStationLoading = true
+ try {
+ // const response = await axios.get(api.location.baseStation)
+ const response = [
+ {
+ longitude: 116.38,
+ latitude: 39.9
+ },
+ {
+ longitude: 116.39,
+ latitude: 39.9
+ },
+ {
+ longitude: 116.383,
+ latitude: 39.886
+ },
+ {
+ longitude: 116.378,
+ latitude: 39.902
+ },
+ {
+ longitude: 116.369,
+ latitude: 39.91
+ },
+ {
+ longitude: 116.38,
+ latitude: 39.91
+ }
+ ]
+ return response // response.data.data.list
+ } catch (e) {
+ this.errorMsgHandler(e)
+ console.error(e)
+ } finally {
+ // this.loading.baseStationLoading = false
+ }
+ return []
+ },
queryData () {
/* return new Promise((resolve, reject) => {
resolve({
@@ -234,9 +397,10 @@ export default {
})
}) */
const params = {
- resource: this.entity.entityName,
+ resource: `'${this.entity.entityName}'`,
startTime: getSecond(this.timeFilter.startTime),
- endTime: getSecond(this.timeFilter.endTime)
+ endTime: getSecond(this.timeFilter.endTime),
+ dateRangeValue: this.timeFilter.dateRangeValue
}
return axios.get(`${api.entity.locationTrack}`, { params })
},
@@ -334,27 +498,38 @@ export default {
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)
- console.info(result)
- const route = this.generateRouteGEOJSON(result)
- const points = this.generatePointsGEOJSON(result)
- this.myChart.getSource('route').setData(route)
- this.myChart.getSource('points').setData(points)
- if (result.length > 0) {
- 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)
+ const { query } = this.$route
+ this.$store.commit('setTimeRangeArray', [this.timeFilter.startTime, this.timeFilter.endTime])
+ this.$store.commit('setTimeRangeFlag', dateRangeValue.value)
+
+ const newUrl = urlParamsHandler(window.location.href, query, {
+ startTime: this.timeFilter.startTime,
+ endTime: this.timeFilter.endTime,
+ range: dateRangeValue.value
})
+ overwriteUrl(newUrl)
+ // 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)
+ // console.info(result)
+ // const route = this.generateRouteGEOJSON(result)
+ // const points = this.generatePointsGEOJSON(result)
+ // this.mapChart.getSource('route').setData(route)
+ // this.mapChart.getSource('points').setData(points)
+ // if (result.length > 0) {
+ // this.mapChart.jumpTo({ center: this.computeMapCenter(result) })
+ // this.mapChart.zoomTo(this.computeMapZoom(result))
+ // }
+ // }).catch(e => {
+ // this.showError = true
+ // console.error(e)
+ // this.errorMsg = this.errorMsgHandler(e)
+ // }).finally(() => {
+ // this.toggleLoading(false)
+ // })
},
async queryTraceTracking () {
const params = {
@@ -454,6 +629,276 @@ export default {
this.trackingSubscriber.scrollEndIndex = endIndex
// 列表距离顶部距离
this.trackingSubscriber.startOffset = scrollTop - (scrollTop % this.scrollInfo.itemSize)
+ },
+ tooltipMouseEnter () {
+ this.tooltip.mouseInMarkerOrTooltip = true
+ },
+ tooltipMouseLeave (event) {
+ if (this.currentMarkerDom && !this.currentMarkerDom.contains(event.relatedTarget)) {
+ this.tooltip.mouseInMarkerOrTooltip = false
+ this.tooltip.showMarkerTooltip = false
+ this.currentMarkerDom.classList.remove('map-marker--hover')
+ }
+ },
+ trackingHexagonMouseEnter () {
+ this.tooltip.mouseIsInPolygon = true
+ },
+ trackingHexagonMouseLeave () {
+ this.tooltip.showPolygonTooltip = false
+ this.tooltip.mouseIsInPolygon = false
+ // 去掉上一块的高亮
+ this.hoverTrigger('trackingHexGrid', this.currentPolygon.id, false)
+ },
+ trackingHexagonMouseMove (e) {
+ const { originalEvent, features } = e
+ if (!this.tooltip.mouseInMarkerOrTooltip) {
+ this.tooltip.showPolygonTooltip = true
+ this.tooltip.type = this.tooltipType.hexagon
+ if (this.tooltip.type === this.tooltipType.hexagon && this.currentPolygon.id && this.currentPolygon.id !== features[0].id) {
+ // 去掉上一块的高亮
+ this.hoverTrigger('trackingHexGrid', this.currentPolygon.id, false)
+ }
+ this.currentPolygon = features[0].properties
+ this.currentPolygon.id = features[0].id
+ this.currentPolygon.location = `${h3ToGeo(this.currentPolygon.hexId)[1]}, ${h3ToGeo(this.currentPolygon.hexId)[0]}`
+ this.tooltip.x = originalEvent.clientX + 15
+ this.tooltip.y = originalEvent.clientY + 5
+
+ // 鼠标滑过高亮
+ this.hoverTrigger('trackingHexGrid', this.currentPolygon.id, true)
+ }
+ },
+ bindMarkerEvent (el, markerData, type) {
+ el.addEventListener('mouseenter', e => {
+ this.currentMarkerDom = el
+ if (type === this.tooltipType.human) {
+ this.currentSubscriber = markerData
+ if (!this.tooltip.mouseInMarkerOrTooltip) {
+ this.tooltip.x = e.clientX + 15 - e.offsetX
+ this.tooltip.y = (e.clientY + 15 - e.offsetY + this.tooltipDomHeight.human) > this.mapDomHeight ? (this.mapDomHeight - this.tooltipDomHeight.human) : (e.clientY + 15 - e.offsetY)
+ }
+ } else if (type === this.tooltipType.baseStation) {
+ this.currentBaseStation = markerData
+ if (!this.tooltip.mouseInMarkerOrTooltip) {
+ this.tooltip.x = e.clientX + 15 - e.offsetX
+ this.tooltip.y = (e.clientY + 15 - e.offsetY + this.tooltipDomHeight.baseStation) > this.mapDomHeight ? (this.mapDomHeight - this.tooltipDomHeight.baseStation) : (e.clientY + 15 - e.offsetY)
+ }
+ }
+
+ this.tooltip.mouseInMarkerOrTooltip = true
+ this.tooltip.type = type
+ this.tooltip.showMarkerTooltip = true
+ el.classList.add('map-marker--hover')
+ })
+ el.addEventListener('mouseleave', event => {
+ const tooltipDom = document.getElementById('tooltip')
+ if (!tooltipDom.contains(event.relatedTarget)) {
+ el.classList.remove('map-marker--hover')
+ this.tooltip.mouseInMarkerOrTooltip = false
+ this.tooltip.showMarkerTooltip = false
+ }
+ })
+ if (type === this.tooltipType.human) {
+ el.addEventListener('click', e => {
+ this.humanMarkers.forEach(m => {
+ m.getElement().classList.remove('map-marker--highlight')
+ })
+ if (this.highlightSubscriber.subscriberId !== markerData.subscriberId) {
+ el.classList.add('map-marker--highlight')
+ this.highlightSubscriber = markerData
+ // 将滚动条跳转到对应位置
+ document.querySelector(`#locationMap-subscriberId-${markerData.subscriberId}`).scrollIntoView({ behavior: 'smooth', block: 'center' })
+ } else {
+ this.highlightSubscriber = {}
+ }
+ })
+ }
+ },
+ // 地图上人图标鼠标悬浮框中点击追踪事件
+ trackSubscriber (subscriber) {
+ const find = this.trackingSubscribers.find(s => s.subscriberId === subscriber.subscriberId)
+ if (!find) {
+ this.trackingSubscribers.push({ ...subscriber, show: false, showLine: false, scrollStartIndex: 1, scrollEndIndex: 6, startOffset: 0, listHeight: 0 })
+ }
+ this.currentShowSubscriber = subscriber
+ this.activeTab = 'traceTracking'
+ this.tooltip.showMarkerTooltip = false
+ },
+ unbindTrackingHexagonEvents () {
+ this.mapChart.off('mouseenter', this.trackingHexagonMouseEnter)
+ this.mapChart.off('mouseleave', this.trackingHexagonMouseLeave)
+ this.mapChart.off('mousemove', this.trackingHexagonMouseMove)
+ },
+ renderTrackingMarker (coordinates) {
+ const el = document.createElement('div')
+ el.className = 'map-tracking-marker'
+ el.innerHTML = `<div class="tracking-marker__inner-circle">${humanSvg}</div>`
+ this.trackingHumanMarker = new maplibregl.Marker({ element: el }).setLngLat(coordinates).addTo(this.mapChart)
+ },
+ renderTrackingHexagon () {
+ if (!this.currentShowSubscriber) {
+ return true
+ }
+ const currentShowSubscriberRecords = this.currentShowSubscriber.trackRecords
+ if (currentShowSubscriberRecords && currentShowSubscriberRecords.length > 0) {
+ // 六边形
+ this.trackingPolygonSourceData = this.hexagonDataConverter(this.currentShowSubscriber.trackRecords)
+ this.mapChart.addSource('trackingHexGrid', {
+ type: 'geojson',
+ data: this.trackingPolygonSourceData
+ })
+ this.mapChart.addLayer({
+ id: 'trackingHexagon',
+ type: 'fill',
+ source: 'trackingHexGrid',
+ layout: {},
+ paint: {
+ 'fill-color': ['get', 'color'],
+ 'fill-opacity': ['case', ['boolean', ['feature-state', 'hover'], false], 0.75, 0.5]
+ }
+ })
+ // 轨迹线
+ const mapLineSourceData = this.mapLineDataConverter()
+ this.mapChart.addSource('trackingLineSource', {
+ type: 'geojson',
+ data: mapLineSourceData
+ })
+ this.mapChart.addLayer({
+ id: 'trackingLine',
+ type: 'line',
+ source: 'trackingLineSource',
+ paint: {
+ 'line-color': 'rgba(222, 52, 52, .8)',
+ 'line-width': 3
+ }
+ })
+ // 最后所在地的图标
+ const coordinate = h3ToGeo(currentShowSubscriberRecords[0].hexId)
+ this.renderTrackingMarker([coordinate[1], coordinate[0]])
+ this.bindTrackingHexagonEvents()
+ }
+ },
+ mapLineDataConverter () {
+ const records = this.currentShowSubscriber.trackRecords
+ const feature = {
+ type: 'Feature',
+ geometry: {
+ type: 'LineString',
+ coordinates: records.map(d => {
+ const cs = h3ToGeo(d.hexId)
+ return [cs[1], cs[0]]
+ })
+ }
+ }
+ return {
+ type: 'FeatureCollection',
+ features: [feature]
+ }
+ },
+ renderMarker (data, type) {
+ let svg
+ if (type === this.tooltipType.baseStation) {
+ svg = baseStationSvg
+ } else if (type === this.tooltipType.human) {
+ svg = humanSvg
+ }
+ try {
+ data.forEach(marker => {
+ if (type === this.tooltipType.human && marker.subscriberDto) {
+ const el = document.createElement('div')
+ el.className = `map-marker map-marker--${type}`
+ if (marker.subscriberId === this.highlightSubscriber.subscriberId) {
+ el.classList.add('map-marker--highlight')
+ }
+ el.innerHTML = svg
+ // 鼠标事件,控制tooltip显示和marker尺寸
+ this.bindMarkerEvent(el, marker, type)
+ const mapMarker = new maplibregl.Marker({ element: el }).setLngLat([marker.subscriberDto.subscriberLongitude, marker.subscriberDto.subscriberLatitude]).addTo(this.mapChart)
+ mapMarker.subscriberId = marker.subscriberId
+ this.humanMarkers.push(mapMarker)
+ } else if (type === this.tooltipType.baseStation) {
+ const el = document.createElement('div')
+ el.className = `map-marker map-marker--${type}`
+ el.innerHTML = svg
+ // 鼠标事件,控制tooltip显示和marker尺寸
+ this.bindMarkerEvent(el, marker, type)
+ const mapMarker = new maplibregl.Marker({ element: el }).setLngLat([marker.longitude, marker.latitude]).addTo(this.mapChart)
+ this.baseStationMarkers.push(mapMarker)
+ }
+ })
+ } catch (e) {
+ console.error(e)
+ }
+ },
+ hexagonDataConverter (data) {
+ const featureCollection = { type: 'FeatureCollection' }
+ // 对hexId去重,将重复的hexId的时间合并
+ const hexagons = []
+ data.forEach(d => {
+ const find = hexagons.find(h => h.hexId === d.hexId)
+ if (!find) {
+ hexagons.push({ hexId: d.hexId, locations: [{ time: d.time, longitude: d.subscriberLongitude, latitude: d.subscriberLatitude }] })
+ } else {
+ if (find.locations.length < 6) {
+ find.locations.push({ time: d.time, longitude: d.subscriberLongitude, latitude: d.subscriberLatitude })
+ }
+ }
+ })
+ featureCollection.features = hexagons.map((d, i) => ({
+ id: i + 100000,
+ type: 'Feature',
+ geometry: {
+ type: 'Polygon',
+ coordinates: [
+ h3ToGeoBoundary(d.hexId, true)
+ ]
+ },
+ properties: {
+ hexId: d.hexId,
+ number: d.number,
+ locations: d.locations,
+ color: [37, 55, 128]
+ }
+ }))
+ return featureCollection
+ },
+ bindTrackingHexagonEvents () {
+ this.mapChart.on('mouseenter', 'trackingHexagon', this.trackingHexagonMouseEnter)
+ this.mapChart.on('mouseleave', 'trackingHexagon', this.trackingHexagonMouseLeave)
+ this.mapChart.on('mousemove', 'trackingHexagon', this.trackingHexagonMouseMove)
+ },
+ hoverTrigger (source, id, hover) {
+ this.mapChart.setFeatureState({ source, id }, { hover })
+ },
+ timelineMouseEnter (subscriber, record) {
+ this.trackingPolygonSourceData.features.forEach(f => {
+ this.hoverTrigger('trackingHexGrid', f.id, false)
+ })
+ if (this.currentShowSubscriber.subscriberId === subscriber.subscriberId) {
+ const find = this.trackingPolygonSourceData.features.find(d => d.properties.hexId === record.hexId)
+ if (find) {
+ this.hoverTrigger('trackingHexGrid', find.id, true)
+ }
+ }
+ },
+ timelineMouseLeave (subscriber, record) {
+ if (this.currentShowSubscriber.subscriberId === subscriber.subscriberId) {
+ const find = this.trackingPolygonSourceData.features.find(d => d.properties.hexId === record.hexId)
+ if (find) {
+ this.hoverTrigger('trackingHexGrid', find.id, false)
+ }
+ }
+ },
+ changeTimeFilterToInitList () {
+ this.trackingSubscriber.scrollStartIndex = 0
+ this.trackingSubscriber.scrollEndIndex = 11
+ this.trackingSubscriber.startOffset = 0
+ this.trackingSubscriber.listHeight = 0
+ // 高度置为0,是为了切换时间后再打开时间线,让滚动条置顶
+ // const timer = setTimeout(() => {
+ // this.trackingSubscriber.listHeight = this.trackingSubscriber.trackRecords.length * this.scrollInfo.itemSize
+ // clearTimeout(timer)
+ // }, 100)
}
},
setup () {
@@ -465,10 +910,10 @@ export default {
const currentPoint = ref({})
const tooltip = ref({
- showTooltip: false
+ showTooltip: false,
+ type: ''
})
const myChart = shallowRef({})
- const mapLevel = ref('2')
const trackingSubscriber = ref({
subscriberId: '',
show: false,
@@ -487,23 +932,131 @@ export default {
const trackingNoData = ref(true)
const showTrackingError = ref(false)
const trackingErrorMsg = ref('')
+ const tooltipDomHeight = {
+ hexagon: 153,
+ baseStation: 153,
+ human: 167
+ }
+ const currentPolygon = ref({})
+ const currentSubscriber = ref({})
+ const trackingHumanMarker = shallowRef({})
+ const mapChart = shallowRef(null)
+ const baseStationMarkers = shallowRef([])
+ const mapConfig = localStorage.getItem(storageKey.mapConfig) ? JSON.parse(localStorage.getItem(storageKey.mapConfig)) : defaultMapConfig
return {
timeFilter,
currentPoint,
tooltip,
myChart,
- maxZoom: 13,
- minZoom: 1,
- center: [116.38, 39.9],
- mapLevel,
+ maxZoom: mapConfig.maxZoom, // 地图最小缩放比例
+ minZoom: mapConfig.minZoom, // 地图最大缩放比例
+ mapLevel: mapConfig.mapLevel, // 地图精度 1、2、3
+ defaultZoom: mapConfig.defaultZoom, // 地图默认缩放比例
+ center: mapConfig.center, // 地图默认中心点。北京:[116.38, 39.9] 纽约:[-73.94539, 40.841843]
trackingSubscriber,
trackingSubscriberList,
scrollInfo,
trackingNoData,
showTrackingError,
- trackingErrorMsg
+ trackingErrorMsg,
+ tooltipDomHeight,
+ currentPolygon,
+ currentSubscriber,
+ trackingHumanMarker,
+ trackingPolygonSourceData: shallowRef({}),
+ mapChart,
+ baseStationMarkers
}
}
}
</script>
+
+<style lang="scss">
+.map-marker {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 20px;
+ height: 20px;
+ border-radius: 50%;
+ cursor: default;
+ padding: 0;
+ transition: height .1s linear, width .1s linear;
+
+ svg {
+ transition: height .1s linear, width .1s linear;
+ fill: #fff;
+ }
+
+ &.map-marker--human {
+ background-color: #233447;
+ cursor: pointer;
+
+ svg {
+ width: 14px;
+ height: 14px;
+ }
+ }
+ &.map-marker--base-station {
+ background-color: #585B5F;
+
+ svg {
+ width: 12px;
+ height: 12px;
+ }
+ }
+ &.map-marker--hover {
+ width: 30px;
+ height: 30px;
+ border: 2px solid rgba(255,255,255,1);
+ z-index: 2;
+
+ &.map-marker--human svg {
+ width: 21px;
+ height: 21px;
+ }
+ &.map-marker--base-station svg {
+ width: 18px;
+ height: 18px;
+ }
+ }
+ &.map-marker--highlight {
+ width: 30px;
+ height: 30px;
+ border: 2px solid #fff;
+ background-color: rgb(204,68,68);
+ z-index: 3;
+
+ svg {
+ width: 21px;
+ height: 21px;
+ }
+ }
+}
+.map-tracking-marker {
+ width: 36px;
+ height: 36px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ animation: pulse 2s infinite;
+ background-color: rgba(204,68,68,0.50);
+ border-radius: 50%;
+
+ .tracking-marker__inner-circle {
+ width: 25px;
+ height: 25px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background-color: rgba(204,68,68,0.80);
+ border-radius: 50%;
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+ }
+}
+</style>