我们知道elementUI里有个走马灯的组件,如下图,每隔指定的时间,就可以实现轮播切换
现在项目需求是顶部的派出所和下面的警务人员名单都是后端接口返回的数据,然后派出所是个可以单击选中且有走马灯效果的tab导航,效果如下:
后端返回的数据结构如下:
policeList: [
{
organName: "赵李桥派出所",
organId: "1",
roleList: [
{
roleName: "村辅",
policeManList: [
{
userRealName: "张三",
roleName: "村辅",
userPhone: "123838732873",
personCount: "8",
placeCount: "0"
}
]
}
]
},
{
organName: "车埔派出所",
organId: "2"
},
{
organName: "官塘派出所",
organId: "3"
}
],
这里走马灯的每一项是一个数组,然后我定死了这个数组放6个派出所,所以我要对后端返给我的数据做分组,也就是在v-for时用的 v-for=“item in Math.ceil(policeList.length / 6)“和v-for=”(item, i) in policeList.slice((item - 1) * 6, item * 6)”,除此之外我还要对“村辅”“辅警”等警务数据进行折叠处理,也就是在对警务人员名单v-for时用的 v-for=“(memberItem,
memberIndex) in peopleItem.policeManList.slice(0, 9)” 和 v-for=“(memberItem2,
memberIndex2) in peopleItem.policeManList.slice(
9,
peopleItem.policeManList.length
)” ,因为用到了饿了么组件Collapse 折叠面板,这样就能实现收起和展开效果。完整代码如下:
<template>
<div class="box">
<div class="top">
<span class="leftTips">警务人员名单</span>
<div class="line"></div>
<div class="rightBox">
<el-carousel
height="30px"
indicator-position="none"
arrow="hover"
:interval="6000"
:autoplay="false"
class="carousel"
>
<el-carousel-item
v-for="item in Math.ceil(policeList.length / 6)"
:key="item"
>
<div
class="child"
v-for="(item, i) in policeList.slice((item - 1) * 6, item * 6)"
:key="i"
@click="selectItem(item, i)"
>
<el-tooltip
class="el-tooltip"
effect="dark"
:content="item.organName"
placement="top"
>
<span
class="name"
:class="activeOrganId == item.organId ? 'activeName' : ''"
>{
{
item.organName }}</span
>
</el-tooltip>
<div
v-if="activeOrganId == item.organId"
class="activeHeng"
></div>
</div>
</el-carousel-item>
</el-carousel>
</div>
</div>
<div class="bottom">
<div class="jobBox" v-if="roleList && roleList.length > 0">
<div v-for="(peopleItem, peopleIndex) in roleList" :key="peopleIndex">
<el-collapse accordion>
<el-collapse-item>
<template slot="title">
<div class="jobTipsBox">
{
{
peopleItem.roleName }}
</div>
<div class="memberList">
<div
class="member"
v-for="(memberItem,
memberIndex) in peopleItem.policeManList.slice(0, 9)"
:key="memberIndex"
>
<el-popover
placement="top-start"
width="240"
trigger="hover"
>
<div
class="popBox"
style=" display: flex;
flex-direction: column;
justify-content: center;align-items:center;line-height: 28px;"
>
<span
class="userName"
style=" font-family: pingfang;
font-size: 1.25rem;"
>{
{
memberItem.userRealName }}</span
>
<span class="roleName"
>角色:{
{
memberItem.roleName }}</span
>
<span class="userPhone"
>联系电话: {
{
memberItem.userPhone }}</span
>
<el-divider></el-divider>
<div
class="bottomBox"
style=" display: flex;
flex-direction: column;
"
>
<div
class="child"
style=" line-height: 1.75rem;
display: flex;
justify-content: space-around;width:15rem"
>
<span>管辖重点人员</span>
<div
:style="{
cursor:
memberItem.personCount &&
Number(memberItem.personCount) > 0
? 'pointer'
: 'auto'
}"
@click="goPeople(memberItem)"
>
<span
:style="{
color:
memberItem.personCount &&
Number(memberItem.personCount) > 0
? '#2c8ef0'
: '#333'
}"
>{
{
memberItem.personCount }}</span
>
<span
><i
:style="{
visibility:
memberItem.personCount &&
Number(memberItem.personCount) > 0
? 'visible'
: 'hidden'
}"
class="el-icon-arrow-right"
></i>
</span>
</div>
</div>
<div
class="child"
style=" line-height: 1.75rem;
display: flex;
justify-content: space-around;width:15rem"
>
<span>管辖重点场所</span>
<div
:style="{
cursor:
memberItem.placeCount &&
Number(memberItem.placeCount) > 0
? 'pointer'
: 'auto'
}"
@click="goPlace(memberItem)"
>
<span
:style="{
color:
memberItem.placeCount &&
Number(memberItem.placeCount) > 0
? '#2c8ef0'
: '#333'
}"
>{
{
memberItem.placeCount }}</span
>
<span
><i
:style="{
visibility:
memberItem.placeCount &&
Number(memberItem.placeCount) > 0
? 'visible'
: 'hidden'
}"
class="el-icon-arrow-right"
></i>
</span>
</div>
</div>
</div>
</div>
<span slot="reference">
{
{
memberItem.userRealName }}</span
>
</el-popover>
</div>
</div>
</template>
<div class="memberList marginLeft">
<div
class="member"
v-for="(memberItem2,
memberIndex2) in peopleItem.policeManList.slice(
9,
peopleItem.policeManList.length
)"
:key="memberIndex2"
>
<span> {
{
memberItem2.userRealName }}</span>
</div>
</div>
</el-collapse-item>
</el-collapse>
<el-divider v-if="peopleIndex != roleList.length - 1"></el-divider>
</div>
</div>
<div class="jobBox" v-else>
<div class="noPaddingBox">
<img src="@/assets/images/new-home/NoPaddingTask.png" alt="" />
<span>暂无警务人员</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
policeList: {
type: Array
}
},
data() {
return {
// policeList: [
// {
// organName: "赵李桥派出所",
// organId: "1",
// roleList: [
// {
// roleName: "村辅",
// policeManList: [
// {
// userRealName: "张三",
// roleName: "村辅",
// userPhone: "123838732873",
// personCount: "8",
// placeCount: "0"
// }
// ]
// }
// ]
// },
// {
// organName: "车埔派出所",
// organId: "2"
// },
// {
// organName: "官塘派出所",
// organId: "3"
// }
// ],
activeOrganId: "",
roleList: []
};
},
created() {
this.activeOrganId =
this.policeList && this.policeList.length > 0
? this.policeList[0].organId
: "";
},
watch: {
activeOrganId: {
handler(newOrganId, oldOrganId) {
let findObj = this.policeList.find(k => {
return k.organId == newOrganId;
});
if (findObj) {
this.$nextTick(() => {
this.roleList = findObj.roleList;
});
}
},
immediate: true
}
},
methods: {
selectItem(item, i) {
if (this.activeOrganId == item.organId) {
} else {
this.activeOrganId = item.organId;
this.$emit("getPolice", item.organId);
}
},
goPeople(memberItem) {
if (memberItem.personCount && Number(memberItem.personCount) == 0) {
} else {
this.$router.push({
path: "/resource/person",
query: {
userId: memberItem.userId
}
});
}
},
goPlace(memberItem) {
if (memberItem.placeCount && Number(memberItem.placeCount) == 0) {
} else {
this.$router.push({
path: "/resource/placement",
query: {
userId: memberItem.userId
}
});
}
}
}
};
</script>
<style lang="less" scoped>
.box {
display: flex;
flex-direction: column;
min-height: 12.1062rem;
.top {
height: 2.75rem;
position: relative;
.leftTips {
position: absolute;
top: 1.125rem;
left: 1.25rem;
height: 1.1875rem;
font-size: 1.25rem;
font-family: pingfang;
font-weight: bold;
color: #333333;
line-height: 1.375rem;
}
.line {
position: absolute;
top: 1.3125rem;
left: 9.4375rem;
width: 1px;
height: 1.125rem;
border: 1px solid #dddddd;
}
/deep/ .rightBox {
position: absolute;
top: 1.375rem;
left: 10.4375rem;
display: flex;
flex-wrap: nowrap;
width: 50rem;
// border: 1px solid red;
.el-carousel {
width: 100%;
.el-carousel__container {
width: 100%;
.el-carousel__arrow {
margin-top: -0.25rem;
width: 1.25rem !important;
height: 1.25rem !important;
}
.el-carousel__item {
padding: 0 3.125rem;
display: flex;
.child {
width: 7.5rem;
position: relative;
font-size: 1.125rem;
font-family: pingfang;
font-weight: 500;
color: #838a9d;
line-height: 1.375rem;
.name {
width: 7.5rem;
position: absolute;
top: 0rem;
left: 0;
text-align: center;
cursor: pointer;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.activeName {
position: absolute;
top: 0rem;
left: 0;
font-weight: bold;
color: #2969ff;
}
.activeHeng {
position: absolute;
// margin-top: 1.5rem;
top: 1.65rem;
left: 50%;
transform: translateX(-50%);
width: 2.25rem;
height: 4px;
background: #2969ff;
}
}
}
}
}
}
}
/deep/ .bottom {
// border: 1px solid blue;
margin: 1.25rem 0;
.jobBox {
padding-left: 1.25rem;
position: relative;
.noPaddingBox {
position: absolute;
top: -2.2rem;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
text-align: center;
img {
width: 8.5rem;
height: 7.0625rem;
}
span {
font-size: 1.125rem;
font-family: pingfang;
font-weight: 500;
color: #333333;
line-height: 1.375rem;
margin-top: 1.0625rem;
}
}
.jobTipsBox {
width: 5rem;
height: 2.5rem;
background: rgba(41, 105, 255, 0.05);
border: 1px solid #1182fb;
border-radius: 4px;
text-align: center;
font-size: 1rem;
font-family: pingfang;
font-weight: 500;
color: #2969ff;
line-height: 2.5rem;
margin-right: 1.25rem;
}
.memberList {
display: flex;
.member {
width: 4rem;
font-size: 1rem;
font-family: pingfang;
font-weight: 400;
color: #3c4353;
line-height: 1.375rem;
margin-right: 1.8rem;
.el-popover {
.popBox {
.userName {
}
.roleName {
}
.userPhone {
}
.el-divider {
}
.bottomBox {
.child {
div {
span {
&:nth-child(1) {
}
&:nth-child(2) {
}
}
}
}
}
}
}
}
}
.marginLeft {
margin-left: 6.25rem;
}
}
}
/deep/ .el-collapse {
border: none;
}
/deep/ .el-collapse-item__wrap {
border: none;
}
/deep/ .el-collapse-item__header {
border-bottom: none;
}
/deep/ .el-divider--horizontal {
width: 95%;
}
}
</style>
这是利用饿了么官方的组件Carousel实现的走马灯,我们自己也可以实现走马灯,如下效果:
这个任务类型统计里一页打算展示10个任务类型,如果超过10个就也有走马灯轮播切换效果,然后鼠标放在文本上还有提示效果,鼠标放在每项任务上时开启自动轮播,鼠标移出时清除定时轮播,由于后端返回的接口数据是没有图片的,所以需要前端处理一下拿到的接口数据并展示,这里用到了定时器,记得在destroyed里clearInterval(this.start)清除定时器哦,然后用到鼠标移入移出事件,taskCountArr是父组件发请求后传过来的源数据,前端需要处理才能展示,完整代码如下:
<template>
<div class="box">
<div class="top">
<span class="leftTips">任务类型统计</span>
</div>
<div class="barBox">
<div class="tabBox" v-if="imgList && imgList.length > 0">
<div
class="SwiperBox"
ref="SwiperBox"
@mouseenter="MouseFun('移入')"
@mouseleave="MouseFun('移出')"
>
<div
class="imgBox"
:style="{ left: `-${leftVal}px`, transition: `${ition}s` }"
>
<div
class="Swiper-item-parent"
v-for="(item, index) in imgList"
:key="index"
>
<div
class="Swiper-item"
:style="{
'margin-bottom':
index == 5 ||
index == 6 ||
index == 7 ||
index == 8 ||
index == 9
? '0'
: '1.25rem'
}"
v-for="(items, indexs) in item"
:key="indexs"
>
<!-- items.imgUrl -->
<img :src="items.imgUrl" alt="" />
<span class="num">{
{
items.num }}</span>
<el-tooltip
class="el-tooltip"
effect="dark"
:content="items.tips"
placement="top"
>
<span class="tips">{
{
items.tips }}</span>
</el-tooltip>
</div>
</div>
<!-- 复制第一张放到最后,以实现无缝无线循环滚动效果 -->
<div
class="Swiper-item-parent"
v-for="(item2, index2) in imgList"
:key="'2023-' + index2"
>
<div
class="Swiper-item"
:style="{
'margin-bottom':
'2023-' + index2 == 5 ||
'2023-' + index2 == 6 ||
'2023-' + index2 == 7 ||
'2023-' + index2 == 8 ||
'2023-' + index2 == 9
? '0'
: '1.25rem'
}"
v-for="(items2, indexs2) in item2"
:key="'2023-' + indexs2"
>
<img :src="items2.imgUrl" alt="" />
<span class="num">{
{
items2.num }}</span>
<el-tooltip
class="el-tooltip"
effect="dark"
:content="items2.tips"
placement="top"
>
<span class="tips">{
{
items2.tips }}</span>
</el-tooltip>
</div>
</div>
</div>
</div>
<!-- 左箭头按钮 -->
<!-- <div class="leftBtn" @click="throttle(PrevFun)">←</div>
右箭头按钮
<div class="rightBtn" @click="throttle(NextFun)">→</div> -->
<!-- 下方指示点容器 -->
<div class="instBox">
<div
@click="instFun(i)"
v-for="(item, i) in imgList.length"
:key="i"
:class="['inst', i == imgShow ? 'instActv' : '']"
></div>
</div>
</div>
<div class="tabBox" v-else>
<div class="noPaddingBox">
<img src="@/assets/images/new-home/NoPaddingTask.png" alt="" />
<span>暂无任务类型</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
startDate: {
type: String
},
endDate: {
type: String
},
taskCountArr: {
type: Array
}
},
data() {
return {
leftVal: 0, // 轮播图盒子的偏移值
flag: true, // 用来节流防止重复点击j
start: null, // 自动执行下一张定的时器
imgWidth: 960, // 在这里填写你需要的图片宽度
ition: 0.8, // 设置轮播图过度时间
imgShow: 0, // 表示当前显示的图片索引
imgList: []
};
},
watch: {
taskCountArr: {
handler(newVal, oldVal) {
// console.log("接收taskCountArr", newVal);
//先清除定时器
clearInterval(this.start);
let imgArr = [
require("../../../../assets/images/new-home/collect/icon01.png"),
require("../../../../assets/images/new-home/collect/icon02.png"),
require("../../../../assets/images/new-home/collect/icon03.png"),
require("../../../../assets/images/new-home/collect/icon04.png"),
require("../../../../assets/images/new-home/collect/icon05.png"),
require("../../../../assets/images/new-home/collect/icon06.png"),
require("../../../../assets/images/new-home/collect/icon07.png"),
require("../../../../assets/images/new-home/collect/icon08.png"),
require("../../../../assets/images/new-home/collect/icon09.png"),
require("../../../../assets/images/new-home/collect/icon10.png")
];
let random = Math.floor(Math.random() * 10); //获取0到9的随机整数。
if (newVal && newVal.length > 0) {
let newArr = newVal.map((item, index) => {
return {
imgUrl: index > 9 ? imgArr[random] : imgArr[index],
num: item.valuetag,
tips: item.keytag
};
});
// 数组分割
let arr = [];
let num = 10; // 填写需要分割成几个一组的数量
if (newArr.length) {
for (let i = 0; i < newArr.length; i += num) {
arr.push(newArr.slice(i, i + num));
}
}
this.$nextTick(() => {
this.imgList = arr;
});
// console.log("TaskCount回显", this.imgList);
if (newArr.length > 10) {
this.autoPlayWay();
}
this.$forceUpdate();
} else {
this.imgList = [];
}
},
immediate: true
}
},
destroyed() {
clearInterval(this.start);
},
methods: {
// 这里定义一个鼠标移入移出事件,鼠标悬停时停止自动轮播,鼠标移出则重新执行自动轮播
MouseFun(type) {
// 停止定时器 // 重新执行定时器
type == "移入" ? clearInterval(this.start) : this.autoPlayWay();
},
// 此为自动轮播定时器
autoPlayWay() {
this.start = setInterval(() => {
this.NextFun();
}, 4000);
},
// 这里通过额外封装的节流函数触发 PrevFun() 和 NextFun(),以达到防止重复点击的效果
throttle(fun) {
if (this.flag) {
this.flag = false;
fun(); // 此为模板中传递进来的PrevFun()或NextFun()函数
setTimeout(() => {
this.flag = true;
}, 1200); // 节流间隔时间
}
},
// 上一张
PrevFun() {
if (this.leftVal == 0) {
// 判断显示的图片 是 第一张时执行
// this.imgList.length是指循环图片数组的图片个数
this.ition = 0; // 将过渡时间变成0,瞬间位移到最后一张图
this.imgShow = this.imgList.length - 1; // 将高亮小点改为最后一张图
this.leftVal = this.imgList.length * this.imgWidth; // 瞬间移动
setTimeout(() => {
// 通过延时障眼法,归原过渡时间,执行真正的“上一张”函数
this.ition = 0.8;
this.leftVal -= this.imgWidth;
}, this.ition * 1000);
} else {
// 判断显示的图片 不是 第一张时执行
this.ition = 0.8;
this.leftVal -= this.imgWidth;
this.imgShow--;
}
},
// 下一张
NextFun() {
// console.log(
// "this",
// this.leftVal,
// (this.imgList.length - 1) * this.imgWidth
// );
if (this.leftVal == (this.imgList.length - 1) * this.imgWidth) {
// 判断显示的项 是 最后一张时执行
this.ition = 0.8;
this.leftVal += this.imgWidth;
this.imgShow = 0;
setTimeout(() => {
this.ition = 0;
this.leftVal = 0;
}, this.ition * 4000);
} else {
// 判断显示的项 不是 最后一张时执行
this.ition = 0.8;
this.leftVal += this.imgWidth;
this.imgShow++;
}
},
// 点击小圆点
instFun(index) {
this.ition = 0.8;
this.leftVal = index * this.imgWidth;
this.imgShow = index;
}
}
};
</script>
<style lang="less" scoped>
.box {
display: flex;
flex-direction: column;
.top {
height: 2.75rem;
position: relative;
.leftTips {
position: absolute;
top: 1.125rem;
left: 1.25rem;
height: 1.1875rem;
font-size: 1.25rem;
font-family: pingfang;
font-weight: bold;
color: #333333;
line-height: 1.375rem;
}
}
.barBox {
// border: 0.5px solid red;
margin-top: 0.8125rem;
.tabBox {
height: 9.75rem;
padding: 0 0.75rem 0 1.25rem;
position: relative;
.noPaddingBox {
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column;
text-align: center;
img {
width: 8.5rem;
height: 7.0625rem;
}
span {
font-size: 1.125rem;
font-family: pingfang;
font-weight: 500;
color: #333333;
line-height: 1.375rem;
margin-top: 1.0625rem;
}
}
/* 图片容器样式 */
.SwiperBox {
position: relative;
width: 60rem;
height: 9.75rem;
box-sizing: border-box;
overflow: hidden;
}
.imgBox {
position: absolute;
top: 0px;
left: 0px;
// min-width: 60rem;
height: 9.75rem;
display: flex;
justify-content: flex-start;
}
.Swiper-item-parent {
display: flex;
flex-wrap: wrap;
width: 60rem;
/* 图片默认样式 */
.Swiper-item {
flex-shrink: 0;
width: 11.25rem;
height: 4.25rem;
// margin-bottom:1.25rem;
margin-right: 0.75rem;
background: #eff3fb;
border-radius: 4px;
display: flex;
position: relative;
img {
position: absolute;
top: 1.25rem;
left: 0.625rem;
width: 2rem;
height: 2rem;
}
.num {
position: absolute;
top: 0.4375rem;
left: 3.25rem;
height: 1.125rem;
font-size: 1.25rem;
font-family: OPPOSANS-H;
font-weight: 800;
color: #333333;
line-height: 1.75rem;
}
.tips {
position: absolute;
top: 2.1rem;
left: 3.25rem;
width: 6.6875rem;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 1rem;
font-family: pingfang;
font-weight: 500;
color: #838a9d;
}
}
}
/* 两个按钮共有的样式,也可直接使用箭头图片替代 */
.leftBtn,
.rightBtn {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(109, 109, 109, 0.445);
color: #fff;
border-radius: 50%;
cursor: pointer;
font-size: 12px;
font-weight: 500;
}
.leftBtn {
left: 10px;
}
.rightBtn {
right: 10px;
}
/* 下方指示器盒子 */
.instBox {
position: absolute;
left: 50%;
margin-top: 0.625rem;
transform: translateX(-50%);
// border: 1px solid red;
display: flex;
}
/* 小圆点 */
.inst {
width: 8px;
height: 8px;
border: 1px solid #838a9d;
margin-right: 0.75rem;
background: #eff3fb;
border-radius: 50%;
cursor: pointer;
}
.inst:last-child {
margin-right: 0px;
}
.instActv {
border: 1px solid #838a9d;
background: #1182fb;
}
#app {
width: 100%;
padding: 120px;
display: flex;
justify-content: center;
padding: 80px 0px;
}
}
}
}
</style>