设计效果:
需求:
图片是九宫格布局,第一排图片之间的箭头为右箭头,第二排箭头为左箭头,以此类推,下箭头需求是以弓字形显示,卡片右侧内容Hover的时候要出现tip
注意最后一排不足三个的时候要隐藏横向箭头,并隐藏下箭头
子组件
html:
<template>
<div class="visitor-track-container">
<div
v-for="(item,i) in list"
:key="i+Math.random()"
class="visitor-track-content"
>
<div
:key="i+ Math.random()"
class="visitor-track-info"
>
<!-- 左箭头 -->
<div class="visitor-track-arrow-left">
<p class="visitor-track-arrow-desc">
<!-- 耗时x天x时x分x秒 -->
{
{item.differenceTime}}
</p>
<p class="arrow-left">
</p>
</div>
<!-- 照片卡片 -->
<div class="visitor-track-box">
<a-avatar
class="visitor-track-img"
shape="square"
:src="item.snapUrl"
@click="handlerImg(item)"
/>
<div class="visitor-track-right">
<p>
<a-tooltip>
<template slot="title">
<!-- 区域信息 -->
{
{item.cameraRegion}}
</template>
{
{item.cameraRegion}}
</a-tooltip>
</p>
<p>
<a-tooltip>
<!-- 摄像头名称 -->
<template slot="title">
{
{item.cameraName}}
</template>
{
{item.cameraName}}
</a-tooltip>
</p>
<p :class="['visitor-track-similarity',item.similarity<=50?'similarity':'']">
<a-tooltip>
<template slot="title">
相似度:{
{item.similarity||0}}%
</template>
相似度:{
{item.similarity||0}}%
</a-tooltip>
</p>
<p>
<a-tooltip>
<template slot="title">
<!-- 时间 -->
{
{item.eventTime}}
</template>
{
{item.eventTime}}
</a-tooltip>
</p>
</div>
<div>
</div>
</div>
<!-- 右箭头 -->
<div class="visitor-track-arrow-right">
<p class="visitor-track-arrow-desc">
<!-- 耗时x天x时x分x秒 -->
{
{item.differenceTime}}
</p>
<p class="arrow-right">
</p>
</div>
</div>
<!-- 下箭头 -->
<div :class="{visitorTrackArrowBottom:true, showRightArrowBottom:(i + 1)%3==0&&(i + 1)%6!=0?true:false, showLeftArrowBottom:i %3==0&&(i)%2!=0?true:false,hideBottomArrowLast:setArrow(i)}">
<i class="arrow-bottom"></i>
<p class="visitor-track-arrow-desc"> {
{item.differenceTime}}</p>
</div>
</div>
</div>
</template>
js:
<script>
export default {
name: 'TrajectoryChart',
props: {
list: {
type: Array,
default: () => []
}
},
data() {
return {}
},
computed: {},
methods: {
// 当最后一行不足3个或刚好三个时去掉最后一行的(左)下箭头
setArrow(i) {
if (this.list.length % 3 == 0) {
if (
i == this.list.length - 1 ||
i == this.list.length - 2 ||
i == this.list.length - 3
) {
return true
}
console.log('余', 0)
} else if (this.list.length % 3 == 1) {
if (i == this.list.length - 1) {
return true
}
console.log('余', 1)
} else if (this.list.length % 3 == 2) {
if (i == this.list.length - 1 || i == this.list.length - 2) {
return true
}
console.log('余', 2)
}
},
// 查看大图
handlerImg(item) {
this.$emit('onImg', item)
}
}
}
</script>
css
.visitor-track-container {
display: flex;
flex-wrap: wrap;
.visitor-track-content {
width: 40%;
display: flex;
flex-direction: column;
// TODO:删除
// 选中1,4,7,10...之间的偶数,2,5,8..之间的偶数,3,6,9...之前的偶数
&:nth-child(3n + 1):nth-child(2n),
&:nth-child(3n + 2):nth-child(odd),
&:nth-child(3n):nth-child(2n) {
color: red;
}
.visitor-track-info {
display: flex;
// align-items: center;
justify-content: space-around;
flex-wrap: wrap;
flex-direction: row;
}
// 照片卡片
.visitor-track-box {
display: flex;
padding: 17px 24px;
background: #f4f4f4;
.visitor-track-img {
width: 120px;
height: 90px;
margin-right: 24px;
&:hover {
cursor: pointer;
}
}
.visitor-track-right {
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 20px;
color: $second-color-text-1;
p {
margin-bottom: 4px;
span {
max-width: 126px;
display: block;
// width: 200px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.visitor-track-similarity {
color: $main-color;
&.similarity {
color: $second-color-text-4;
}
}
}
}
// 右箭头box
.visitor-track-arrow-right {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 16px;
.visitor-track-arrow-desc {
margin-bottom: 16px;
}
.arrow-right {
position: relative;
width: 140px;
min-width: 100%;
height: 0px;
border: 2px solid #dddddd;
}
.arrow-right::before {
content: '';
position: absolute;
right: -16px;
top: -10px;
border: 10px solid transparent;
border-left: 20px solid #dddddd;
width: 0;
height: 0px;
}
}
// 左箭头
.visitor-track-arrow-left {
display: flex;
flex: 1;
align-items: center;
justify-content: center;
flex-direction: column;
padding: 0 16px;
.visitor-track-arrow-desc {
margin-bottom: 16px;
}
.arrow-left {
position: relative;
width: 140px;
min-width: 100%;
height: 0px;
border: 2px solid #dddddd;
}
.arrow-left::before {
content: '';
position: absolute;
right: -16px;
top: -10px;
width: 0;
height: 0px;
left: -16px !important;
border: 10px solid transparent;
border-right: 20px solid #dddddd;
}
}
// 下箭头
.visitorTrackArrowBottom {
display: none;
align-items: center;
margin: 15px 0 15px 162px;
.arrow-bottom {
position: relative;
height: 50px;
border: 2px solid #dddddd;
&::before {
content: '';
position: absolute;
right: -10px;
bottom: -15px;
border: 10px solid transparent;
border-top: 20px solid #dddddd;
width: 0;
height: 0px;
}
}
.visitor-track-arrow-desc {
margin-left: 16px;
font-family: 'PingFang SC';
font-style: normal;
font-weight: 400;
font-size: 14px;
line-height: 20px;
text-align: left;
color: $second-color-text-1;
}
&.showRightArrowBottom {
display: flex;
}
&.showLeftArrowBottom {
display: flex;
}
}
// 3的倍数下隐藏横向箭头
&:nth-child(3n + 3) {
.visitor-track-arrow-right {
display: none;
}
}
// 隐藏奇排倍数的右箭头
&:nth-child(3n + 1):nth-child(2n),
&:nth-child(3n + 2):nth-child(odd),
&:nth-child(3n):nth-child(2n) {
.visitor-track-arrow-right {
display: none;
}
}
// 隐藏偶排的左箭头
&:not(
:nth-child(3n + 1):nth-child(2n),
:nth-child(3n + 2):nth-child(odd),
:nth-child(3n):nth-child(2n)
) {
color: green;
.visitor-track-arrow-left {
display: none;
}
}
//显示右箭头 3的倍数中visitor-track-content占20%宽,选择奇数
&:nth-child(3n):nth-child(odd) {
width: 20%;
}
//显示左箭头 1,4,7,倍数中占宽20%选择奇数,并隐藏左箭头
&:nth-child(3n + 1):nth-child(2n) {
width: 20%;
.visitor-track-arrow-left {
display: none;
}
}
// 最后一排隐藏下箭头,清除最后一个卡片的居中布局
&:last-child {
.visitor-track-info {
justify-content: initial;
}
.visitor-track-arrow-right {
display: none;
}
}
.hideBottomArrowLast {
display: none !important;
}
}
@media screen and (max-width: 1840px) {
.visitor-track-content {
width: 38.5%;
&:nth-child(3n):nth-child(odd) {//为3的倍数下的奇数
width: 21.5%;
}
// //显示左箭头 1,4,7,倍数中占宽20%选择奇数,并隐藏左箭头
&:nth-child(3n + 1):nth-child(2n) {
width: 21.5%;
}
}
}
@media screen and (max-width: 1730px) {
.visitor-track-content {
width: 38.5%;
&:nth-child(3n):nth-child(odd) {//为3的倍数下的奇数
width: 22.5%;
}
// //显示左箭头 1,4,7,倍数中占宽20%选择奇数,并隐藏左箭头
&:nth-child(3n + 1):nth-child(2n) {
width: 22.5%;
}
}
}
@media screen and (max-width: 1670px) {
.visitor-track-content {
width: 38.5%;
&:nth-child(3n):nth-child(odd) {//为3的倍数下的奇数
width: 23%;
}
// //显示左箭头 1,4,7,倍数中占宽20%选择奇数,并隐藏左箭头
&:nth-child(3n + 1):nth-child(2n) {
width: 23%;
}
}
}
@media screen and (max-width: 1600px) {
.visitor-track-content {
width: 37%;
//显示右箭头3的倍数中visitor-track-content占20%宽,选择奇数
&:nth-child(3n):nth-child(odd) {
width: 24.5%;
}
//显示左箭头 1,4,7,倍数中占宽20%选择奇数
&:nth-child(3n + 1):nth-child(2n) {
width: 24.5%;
}
.arrow-right,.arrow-left {
min-width: 100px !important;
width: 100px !important;
}
}
}
@media screen and (max-width: 1510px) {
.visitor-track-box {
padding: 17px 18px !important;
.visitor-track-right {
p {
span {
max-width: 108px !important;
display: block;
}
}
}
}
.visitor-track-img {
width: 110px !important;
margin-right: 16px !important;
}
}
}
父组件内容
引入子组件并使用,我这里在弓字的时候因为后台没有对2倍数的排数倒序所以这里要处理一下让2倍的数据倒过来
<!--访客轨迹 -->
<template>
<div class="xk-container">
<trajectory-chart :list="trajectoryList" @onImg="handlerImg" />
</div>
</template>
<script>
import TrajectoryChart from '@/components/TrajectoryChart'
import test from './../test.json'
export default {
name: 'VisitorTrack',
components: {
TrajectoryChart
},
data() {
return {
trajectoryList: [],
}
},
mounted() {
//调用接口,这里是模拟数据
const doubleArr = []
for (var i = 0; i < test.data.snapInfoList.length; i += 3) { // 3个为一组转为二维数组
doubleArr.push(test.data.snapInfoList.slice(i, i + 3))
}
// 对奇数组进行倒序
const doubleArrReverse = doubleArr.map((item, i) => i % 2 !== 0 ? item.reverse() : item)
console.log('对二的倍数数组进行倒序', doubleArrReverse)
const linearArr = doubleArrReverse.reduce((a, b) => { return a.concat(b) })// 转一维数组
this.trajectoryList = linearArr.map((item, i) => {
return {
...item
}
})
},
}
}
</script>
json数据 test.json
{
"respCode": "200",
"respMsg": "操作成功",
"data": {
"guestId": "20221031d1fed47d519f4a68a4e18b02",
"guestName": "唐兰溪",
"visitedUserName": "黄小访",
"avatar": "/gzcss-boot/zhfkfksq/20221203ebrza6t1.jpg",
"reson": "拜访领导",
"startTime": "2022-12-04 08:00:00",
"endTime": "2022-12-04 10:00:00",
"visitDepart": "总务部",
"company": "广州xx",
"snapInfoList": [
{
"cameraCode": "5dea797fc94e44e0a528aad02fc36e70",
"bkgUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDQ3MzIxNjAwMDAxMzExNTAmc2l6ZT0xMzExNTA=&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6RDhsSkZTYVFrVGNFZ0VrMFA3NFVDbDdpMStoSzFNSSsrN3g1SXBJdDJ0ND06MTY4MDA4MDE5NzY3Ng==&urlType=1",
"snapUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDQ3MzAxMTIwMDAwMTc4Nzgmc2l6ZT0xNzg3OA==&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6ckpnVEtJOXNQTW13QTlrWmlLQW55MmtZejFIQmNLOCtjUXVqWHM5bzY2RT06MTY4MDA4MDE5NzY4Mw==&urlType=1",
"eventTime": "2023/01/04 08:41:01",
"locLng": null,
"locLat": null,
"locAltitude": null,
"cameraName": null,
"similarity": "88%",
"differenceTime": "耗时:6小时53分钟44秒。"
},
{
"cameraCode": "5dea797fc94e44e0a528aad02fc36e70",
"bkgUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDc2MzAwODAwMDAxNjUzOTkmc2l6ZT0xNjUzOTk=&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6dVZrYjFvOW1GOVo2K3NjMS9KaGIwb1d5dzJvU3BObDhkMEk0UXR3bHBnaz06MTY4MDA4MDE5NzYwNw==&urlType=1",
"snapUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDc2Mjc2MjI0MDAwMjM2ODYmc2l6ZT0yMzY4Ng==&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6U253QVpVT0xiakFHTUNyM1ZLUVlFeEdTeXZseHA4TFJFWDJieFJUZ093Yz06MTY4MDA4MDE5NzYxOQ==&urlType=1",
"eventTime": "2023/01/04 15:34:45",
"locLng": null,
"locLat": null,
"locAltitude": null,
"cameraName": null,
"similarity": "90%",
"differenceTime": "耗时:19秒。"
},
{
"cameraCode": "5dea797fc94e44e0a528aad02fc36e70",
"bkgUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDc2NTE3ODg4MDAxNjc5MzMmc2l6ZT0xNjc5MzM=&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6QU5uRkwrNnRGN1lYOEU2SkpkSG5PVE9OS3Y2YXpaNHRaUndURXVXKzE4UT06MTY4MDA4MDE5NzM3Nw==&urlType=1",
"snapUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDc2NDY4NzM2MDAwNDYxMDUmc2l6ZT00NjEwNQ==&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6OXBUWjZ2ZVlVQTh5TU5SNkErMEtvWlRwWnBVd21oR083ckVWSEFhTE9saz06MTY4MDA4MDE5NzM4Ng==&urlType=1",
"eventTime": "2023/01/04 15:35:04",
"locLng": null,
"locLat": null,
"locAltitude": null,
"cameraName": null,
"similarity": "94%",
"differenceTime": "耗时:27分钟14秒。"
},
{
"cameraCode": "5dea797fc94e44e0a528aad02fc36e70",
"bkgUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDc2ODkwNjI0MDAxNTczOTQmc2l6ZT0xNTczOTQ=&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6Nm85TW05ZkVVVXBKd0RSOGdUU2RNNFFQTmVvV0lCZ0pLeUVYUUF0bVJsaz06MTY4MDA4MDE5NzMyMQ==&urlType=1",
"snapUrl": "https://192.168.8.36:8376/uis/service/v1/picture/stream/fetch?imgSrc=aHR0cDovLzE5Mi4xNjguOC40MDo4MC9waWN0dXJlL1N0cmVhbWluZy90cmFja3MvMTAzLz9uYW1lPWNoMDAwMDFfMDAwMDAwMDAwMTcwMDc2ODY2MDQ4MDAwMjA4MTMmc2l6ZT0yMDgxMw==&signature=SElLIDZFT1MvZTBqWXI5N3ViWlo6NVpYbzZKNzFsYlVJeGl4elZ2dVZFaS9VbVY3OGExMWp5WjAwRXRSaUVZOD06MTY4MDA4MDE5NzM2OQ==&urlType=1",
"eventTime": "2023/01/04 16:02:18",
"locLng": null,
"locLat": null,
"locAltitude": null,
"cameraName": null,
"similarity": "94%",
"differenceTime": "耗时:10秒。"
},
]
},
"success": true
}
效果图