CN-1573 feat: 轨迹地图-追踪页时间轴开发
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
@font-face {
|
||||
font-family: "cn-icon"; /* Project id 2614877 */
|
||||
src: url('iconfont.woff2?t=1706606024800') format('woff2'),
|
||||
url('iconfont.woff?t=1706606024800') format('woff'),
|
||||
url('iconfont.ttf?t=1706606024800') format('truetype');
|
||||
src: url('iconfont.woff2?t=1709283742450') format('woff2'),
|
||||
url('iconfont.woff?t=1709283742450') format('woff'),
|
||||
url('iconfont.ttf?t=1709283742450') format('truetype');
|
||||
}
|
||||
|
||||
.cn-icon {
|
||||
@@ -13,6 +13,14 @@
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.cn-icon-down2:before {
|
||||
content: "\ec0b";
|
||||
}
|
||||
|
||||
.cn-icon-up2:before {
|
||||
content: "\ec0c";
|
||||
}
|
||||
|
||||
.cn-icon-license:before {
|
||||
content: "\e666";
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -63,7 +63,7 @@ export default {
|
||||
if (!markDom) {
|
||||
const childDiv = document.createElement('div')
|
||||
childDiv.className = 'time-line-mark'
|
||||
childDiv.style.cssText += 'width: 0.5px;height: 40px;background: #000;margin-left: 18px;margin-top: -5px;'
|
||||
childDiv.style.cssText += 'width: 0.5px;height: 40px;background: #000;margin-left: 18px;margin-top: -7px;'
|
||||
blockDom.appendChild(childDiv)
|
||||
}
|
||||
})
|
||||
@@ -209,8 +209,8 @@ export default {
|
||||
if (this.timeLine[e]) {
|
||||
// 返回所选时间后一分钟
|
||||
const timeObj = {
|
||||
startTime: this.timeLine[e].stamp,
|
||||
endTime: this.timeLine[e].stamp - 60 * 1000
|
||||
startTime: this.timeLine[e].stamp - 60 * 1000,
|
||||
endTime: this.timeLine[e].stamp
|
||||
}
|
||||
this.$emit('change', timeObj)
|
||||
}
|
||||
@@ -234,7 +234,7 @@ export default {
|
||||
.time-line-slider {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
top: -8px;
|
||||
top: -6px;
|
||||
|
||||
:deep .el-slider__runway {
|
||||
//width: calc(100% - 18px);
|
||||
@@ -251,7 +251,7 @@ export default {
|
||||
height: 0;
|
||||
border-color: #000 rgba(0, 0, 0, 0) rgba(0, 0, 0, 0) rgba(0, 0, 0, 0);
|
||||
border-style: solid;
|
||||
border-width: 10px 10px 0 10px;
|
||||
border-width: 8px 8px 0 8px;
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
border-radius: 0;
|
||||
}
|
||||
@@ -261,7 +261,7 @@ export default {
|
||||
.time-line-container {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
background-color: rgba(255, 255, 255, 0.60);
|
||||
background-color: rgba(255, 255, 255, 0.50);
|
||||
//background-color: rgb(89, 173, 201);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
112
src/components/common/collapse.vue
Normal file
112
src/components/common/collapse.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<transition
|
||||
:name="transitionName"
|
||||
@before-enter="collapseBeforeEnter"
|
||||
@enter="collapseEnter"
|
||||
@after-enter="collapseAfterEnter"
|
||||
@before-leave="collapseBeforeLeave"
|
||||
@leave="collapseLeave"
|
||||
@after-leave="collapseAfterLeave">
|
||||
<slot></slot>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 元素折叠过度效果
|
||||
*/
|
||||
export default {
|
||||
name: 'CollapseTransition',
|
||||
props: {
|
||||
transitionName: {
|
||||
type: String,
|
||||
default: 'collapse-transition'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
oldPaddingTop: '',
|
||||
oldPaddingBottom: '',
|
||||
oldOverflow: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
collapseBeforeEnter (el) {
|
||||
// console.log('11, collapseBeforeEnter')
|
||||
this.oldPaddingBottom = el.style.paddingBottom
|
||||
this.oldPaddingTop = el.style.paddingTop
|
||||
// 过渡效果开始前设置元素的maxHeight为0,让元素maxHeight有一个初始值
|
||||
el.style.paddingTop = '0'
|
||||
el.style.paddingBottom = '0'
|
||||
el.style.maxHeight = '0'
|
||||
},
|
||||
collapseEnter (el, done) {
|
||||
// console.log('22, collapseEnter')
|
||||
this.oldOverflow = el.style.overflow
|
||||
const elHeight = el.scrollHeight
|
||||
// 过渡效果进入后将元素的maxHeight设置为元素本身的高度,将元素maxHeight设置为auto不会有过渡效果
|
||||
if (elHeight > 0) {
|
||||
el.style.maxHeight = elHeight + 'px'
|
||||
} else {
|
||||
el.style.maxHeight = '0'
|
||||
}
|
||||
el.style.paddingTop = this.oldPaddingTop
|
||||
el.style.paddingBottom = this.oldPaddingBottom
|
||||
|
||||
el.style.overflow = 'hidden'
|
||||
const onTransitionDone = function () {
|
||||
done()
|
||||
el.removeEventListener('transitionend', onTransitionDone, false)
|
||||
el.removeEventListener('transitioncancel', onTransitionDone, false)
|
||||
}
|
||||
// 绑定元素的transition完成事件,在transition完成后立即完成vue的过度动效
|
||||
el.addEventListener('transitionend', onTransitionDone, false)
|
||||
el.addEventListener('transitioncancel', onTransitionDone, false)
|
||||
},
|
||||
collapseAfterEnter (el) {
|
||||
// 过渡效果完成后恢复元素的maxHeight
|
||||
el.style.maxHeight = ''
|
||||
el.style.overflow = this.oldOverflow
|
||||
},
|
||||
|
||||
collapseBeforeLeave (el) {
|
||||
this.oldPaddingBottom = el.style.paddingBottom
|
||||
this.oldPaddingTop = el.style.paddingTop
|
||||
this.oldOverflow = el.style.overflow
|
||||
|
||||
el.style.maxHeight = el.scrollHeight + 'px'
|
||||
el.style.overflow = 'hidden'
|
||||
},
|
||||
collapseLeave (el, done) {
|
||||
if (el.scrollHeight !== 0) {
|
||||
el.style.maxHeight = '0'
|
||||
el.style.paddingBottom = '0'
|
||||
el.style.paddingTop = '0'
|
||||
}
|
||||
const onTransitionDone = function () {
|
||||
done()
|
||||
el.removeEventListener('transitionend', onTransitionDone, false)
|
||||
el.removeEventListener('transitioncancel', onTransitionDone, false)
|
||||
}
|
||||
// 绑定元素的transition完成事件,在transition完成后立即完成vue的过度动效
|
||||
el.addEventListener('transitionend', onTransitionDone, false)
|
||||
el.addEventListener('transitioncancel', onTransitionDone, false)
|
||||
},
|
||||
collapseAfterLeave (el) {
|
||||
el.style.maxHeight = ''
|
||||
el.style.overflow = this.oldOverflow
|
||||
el.style.paddingBottom = this.oldPaddingBottom
|
||||
el.style.paddingTop = this.oldPaddingTop
|
||||
|
||||
this.oldOverflow = this.oldPaddingBottom = this.oldPaddingTop = ''
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.collapse-transition-enter-active,
|
||||
.collapse-transition-leave-active {
|
||||
transition: height .3s ease-in-out, padding .3s ease-in-out, max-height .3s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
@@ -83,7 +83,7 @@
|
||||
<!-- 右侧数据栏-trace -->
|
||||
<div class="analysis-statistics" v-show="activeTab === 'traceTracking'">
|
||||
<div class="analysis-statistics__subscribers">
|
||||
<template v-for="subscriber in trackingSubscribers" :key="subscriber.subscriberId">
|
||||
<template v-for="(subscriber, index) in trackingSubscribers" :key="subscriber.subscriberId">
|
||||
<div class="analysis-statistics__subscriber">
|
||||
<div class="subscriber__header">
|
||||
<div class="header__icon">
|
||||
@@ -103,6 +103,65 @@
|
||||
<div class="item__label">Info</div>
|
||||
<div class="item__value">Leader</div>
|
||||
</div>
|
||||
<div class="body__item">
|
||||
<div class="item__label">Home</div>
|
||||
<div class="item__value">China, Shanghai,FACAI District,9527</div>
|
||||
</div>
|
||||
<div class="body__item">
|
||||
<div class="item__label">Company</div>
|
||||
<div class="item__value">China, Shanghai,FACAI District,9527</div>
|
||||
</div>
|
||||
<div class="body-item-record">
|
||||
<div class="item-record__header">Track record</div>
|
||||
<div class="item-record__info">
|
||||
<div class="circle"></div>
|
||||
</div>
|
||||
<div class="item-record__timeline">
|
||||
<div class="timeline__info">
|
||||
<div class="timeline__info--circle">
|
||||
<div class="info__circle"></div>
|
||||
<div class="info__line" v-show="subscriber.showLine"></div>
|
||||
</div>
|
||||
<div class="timeline__info--item">
|
||||
<div>
|
||||
<span>Location: </span><span class="info--item__value">124123</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Time of arrival: </span><span class="info--item__value">2024-01-09 14:23:41</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Residence Time: </span><span class="info--item__value">1 Hour</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<collapse>
|
||||
<el-timeline v-show="subscriber.show" style="transition: all 0.2s">
|
||||
<el-timeline-item
|
||||
v-for="(activity, index) in activities"
|
||||
:key="index"
|
||||
:color="activity.color"
|
||||
>
|
||||
<div class="timeline__item" v-if="activity.show">
|
||||
<div>
|
||||
<span>Location: </span><span class="item__value">124123</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Time of arrival: </span><span class="item__value">2024-01-09 14:23:41</span>
|
||||
</div>
|
||||
<div>
|
||||
<span>Residence Time: </span><span class="item__value">1 Hour</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</collapse>
|
||||
|
||||
<div class="item-record__btn" @click="clickTrackBlock(index)">
|
||||
<span v-if="!subscriber.show"><i class="cn-icon cn-icon-down2"></i></span>
|
||||
<span v-if="subscriber.show"><i class="cn-icon cn-icon-up2"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -216,6 +275,7 @@ import { urlParamsHandler, overwriteUrl } from '@/utils/tools'
|
||||
import axios from 'axios'
|
||||
import { api } from '@/utils/api'
|
||||
import { h3ToGeo, h3ToGeoBoundary } from 'h3-js'
|
||||
import collapse from '../../components/common/collapse'
|
||||
|
||||
export default {
|
||||
name: 'Location',
|
||||
@@ -227,9 +287,13 @@ export default {
|
||||
human: 'human'
|
||||
},
|
||||
activeCount: '',
|
||||
activeCountChain: ''
|
||||
activeCountChain: '',
|
||||
activeNames: ''
|
||||
}
|
||||
},
|
||||
components: {
|
||||
collapse
|
||||
},
|
||||
methods: {
|
||||
valueToRangeValue,
|
||||
async initMap () {
|
||||
@@ -653,6 +717,17 @@ export default {
|
||||
} else {
|
||||
this.timeFilter = JSON.parse(JSON.stringify(this.timeFilter))
|
||||
}
|
||||
},
|
||||
clickTrackBlock (i) {
|
||||
this.trackingSubscribers[i].show = !this.trackingSubscribers[i].show
|
||||
if (this.trackingSubscribers[i].show) {
|
||||
this.trackingSubscribers[i].showLine = true
|
||||
} else {
|
||||
const timer = setTimeout(() => {
|
||||
this.trackingSubscribers[i].showLine = false
|
||||
clearTimeout(timer)
|
||||
}, 200)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -762,7 +837,7 @@ export default {
|
||||
localStorage.getItem(storageKey.trackingSubscriberIds) && (trackingSubscribers.value = JSON.parse(localStorage.getItem(storageKey.trackingSubscriberIds)).map(id => ({ subscriberId: id })))
|
||||
const test = ['gary6411', 'test6431', 'test6430', 'test6422']
|
||||
test.forEach(id => {
|
||||
trackingSubscribers.value.push({ subscriberId: id })
|
||||
trackingSubscribers.value.push({ subscriberId: id, show: false, showLine: false })
|
||||
})
|
||||
|
||||
const currentShowSubscriber = ref(null)
|
||||
@@ -776,6 +851,38 @@ export default {
|
||||
pieLoading: true, // 控制饼图加载状态
|
||||
lineLoading: true // 控制折线图加载状态
|
||||
})
|
||||
const activities = ref([
|
||||
{
|
||||
content: 'Event start',
|
||||
timestamp: '2018-04-15',
|
||||
color: '#DE3434',
|
||||
show: false
|
||||
},
|
||||
{
|
||||
content: 'Approved',
|
||||
timestamp: '2018-04-13',
|
||||
color: '#DE3434',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
content: 'Success',
|
||||
timestamp: '2018-04-11',
|
||||
color: '#DE3434',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
content: 'Success',
|
||||
timestamp: '2018-04-11',
|
||||
color: '#DE3434',
|
||||
show: true
|
||||
},
|
||||
{
|
||||
content: 'Success',
|
||||
timestamp: '2018-04-11',
|
||||
color: '#DE3434',
|
||||
show: true
|
||||
}
|
||||
])
|
||||
return {
|
||||
activeTab,
|
||||
timeFilter,
|
||||
@@ -802,17 +909,18 @@ export default {
|
||||
minZoom: 3, // 地图最大缩放比例
|
||||
unitTypes,
|
||||
defaultZoom: 13, // 地图默认缩放比例
|
||||
center: [116.38, 39.9] // 地图默认中心点。北京:[116.38, 39.9] 纽约:[-73.94539, 40.841843]
|
||||
center: [116.38, 39.9], // 地图默认中心点。北京:[116.38, 39.9] 纽约:[-73.94539, 40.841843]
|
||||
activities
|
||||
}
|
||||
},
|
||||
unmounted () {
|
||||
if (this.mapChart.remove) {
|
||||
this.mapChart && this.mapChart.remove()
|
||||
}
|
||||
if (this.pieChart.dispose) {
|
||||
if (this.pieChart && this.pieChart.dispose) {
|
||||
this.pieChart && this.pieChart.dispose()
|
||||
}
|
||||
if (this.lineChart.dispose) {
|
||||
if (this.lineChart && this.lineChart.dispose) {
|
||||
this.lineChart && this.lineChart.dispose()
|
||||
}
|
||||
}
|
||||
@@ -1075,6 +1183,91 @@ export default {
|
||||
color: #233447;
|
||||
}
|
||||
}
|
||||
|
||||
.body-item-record {
|
||||
.item-record__header {
|
||||
font-family: Helvetica;
|
||||
font-size: 16px;
|
||||
color: #353636;
|
||||
font-weight: 400;
|
||||
height: 38px;
|
||||
line-height: 38px;
|
||||
}
|
||||
.item-record__info {
|
||||
|
||||
}
|
||||
.item-record__timeline {
|
||||
.el-timeline {
|
||||
padding-left: 0;
|
||||
.el-timeline-item {
|
||||
padding-bottom: 4px;
|
||||
.el-timeline-item__tail {
|
||||
border-left: 2px dotted #cccccc;
|
||||
margin-left: 2px;
|
||||
}
|
||||
.el-timeline-item__node--normal {
|
||||
background-image: radial-gradient(#DE3434 20%, transparent);
|
||||
outline: #F7F7F7 solid 6px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
.el-timeline-item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
.timeline__info {
|
||||
display: flex;
|
||||
.timeline__info--circle {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.info__circle {
|
||||
width: 17px;
|
||||
height: 17px;
|
||||
margin-left: -2px;
|
||||
border-radius: 50%;
|
||||
background-image: radial-gradient(#DE3434 20%, transparent);
|
||||
outline: rgba(222,52,52,0.30) solid 4px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.info__line {
|
||||
border-left: 2px #cccccc dotted;
|
||||
height: 34px;
|
||||
margin-left: 6px;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
}
|
||||
.timeline__info--item {
|
||||
padding-left: 13px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
|
||||
.info--item__value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.timeline__item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
font-size: 12px;
|
||||
color: #666666;
|
||||
|
||||
.item__value {
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
.item-record__btn {
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user