如果你的页面是一个数组直接遍历的,那么只看 if 里面 active == 0 里的代码就行了,另一种是是对象的
点击其他区域会关闭提示框,点击另一个问号按钮会显示另一个提示框,关闭当前提示框
主要思路
在最外层view添加点击事件,用来关闭提示框,显示按钮添加点击事件(catchtap)执行显示方法并且可以关闭提示框,主要用到事件冒泡和事件捕获(按钮)
wxml
wxmlwxml
<van-tabs active="{{active}}" bind:change="tabChange">
<van-tab title="营收" >
<view class="data-center" bindtap="hideTip" >
<view class="data-info" wx:for="{{dataInfo}}" wx:key="index" wx:for-index="index">
<h4 class="data-name" >{{item.name}}</h4>
<h1 class="bold data">
{{item.name== '学费收入'|| item.name== '课时单价' || item.name== '退费金额' ? '¥' : '' }}{{item.name== '学费收入'|| item.name== '课时单价' || item.name== '退费金额'? item.cur/100 : item.cur }}
<text class="unit" wx:if="{{item.name == '报名人数' || item.name == '正式学员' || item.name == '试听学员' || item.name == '退费人数' }}" style="font-weight: normal;" decode="{{true}}" space="{{true}}"> 人</text>
</h1>
<h5 class="dataAdd">较前一{{titleActive == 1? '周': titleActive == 2? '月': '天'}} {{item.add}}
<image class="" src="{{ item.cur - item.old >=0 ? '/icons/zengzhang.png': '/icons/xiajiang.png'}}" >
</image>
</h5>
<!--提示框部分 使用catchtap,也要为提示添加catchtap,,防止向上冒泡导致提示框关闭,因为我们点击提示框是不需要关闭提示框的 -->
<image class="doubt" src="/icons/doubt.png" catchtap="showTip" data-idx="{{index}}" data-active="0" ></image>
<view class="tip" wx:if="{{item.showTip}}" catchtap="clickTip" >
<h5>{{item.tip}}</h5>
</view>
</view>
</view>
</van-tab>
<!-- 对象的方式,如果是数组遍历渲染的,可以不看这部分-->
<van-tab title="考勤">
<!-- 我只截取了按钮部分的代码-->
<!-- 可以看到点击事件带了一个 name参数 -->
<image class="doubt" src="/icons/doubt.png" catchtap="showTip" data-name="tCountTip" ></image>
<!-- 判断提示框的条件是 一个公共变量 currentNameTip 是否与 name 参数的值 相等 -->
<view class="tip" wx:if="{{currentNameTip == dataInfo.tCountTip}}" catchtap="clickTip" >
<h5>老师数</h5>
</view>
</van-tab>
</van-tabs>
js
js js
showTip(e) { // 按钮点击显示提示框,再次点击会关闭 (事件捕获,不能冒泡至关闭方法)
/** 因为我这个页面一共四个tab页,第一个页面的数组渲染,其他三个后端返回的都是对象,
所以依靠active == 0 来判断第一个页面,用数组渲染和对象渲染,应该使用不同的方法,
但是我另外三个页面都是一个大的里面的,我得挨个添加 不同的标识,如果是100个参数我不就得挨个写到猴年??? (各位如果有更好的方法可以提供一下,谢谢~~)
*/
const active = e.currentTarget.dataset.active?e.currentTarget.dataset.active: undefined
const idx = e.currentTarget.dataset.idx // 拿到点击的当前的 按钮的 下标 index
// name:使用对象渲染的时候会用到,现在是后端返回的数组,直接遍历渲染
const name = e.currentTarget.dataset.name?e.currentTarget.dataset.name: undefined
if(active) { // 如果来自第一个页面
if(this.data.dataInfo[idx].showTip) { // 为数组内当前下标里对象添加一个显示标识, 为 showTip,如果存在 showTip,表示显示了提示框,那么再次点击将关闭
this.data.dataInfo[idx].showTip = !this.data.dataInfo[idx].showTip // 因为再次点击需要关闭,所以直接用 ! 改变
} else { // 如果不存在 showTip ,则表示当前没有显示提示框,点击后,应该显示当前item内的提示框,关闭其他兄弟元素的提示框
this.data.dataInfo.forEach((item, i) => {
item.showTip = idx == i? true: false
})
}
} else { // 对使用对象进行渲染的提示框进行处理
this.data.dataInfo[name] = name // 将拿到的name进行赋值,新增一个属性,作为这个框的唯一标识
// 这里先在 data里把 currentNameTip 定义为 '',
/** 如果 currentNameTip 和传进来的 name标识相等,那么标识 已经显示提示框,再次点击的话,将currentNameTip 设为空,会关闭当前提示框,
如果不相等,那表示当前没有显示提示框,那么为currentNameTip 赋值为 name, wxml判断这两个变量相等,则会显示提示框 */
if(this.data.currentNameTip !== name) this.data.currentNameTip = name
else this.data.currentNameTip = ''
this.setData({currentNameTip: this.data.currentNameTip})
/**给 currentNameTip 赋值后,wxml里拿这个参数作判断,
如果和 name值相等,那显示提示框,如果不相等,则不显示提示框*/
}
this.setData({
dataInfo: this.data.dataInfo, // 触发视图更新
tipIdx: idx, // 将下标赋值,hedeTip()方法会用到,也可以不写在setData里
name: name // 将name 赋值, hedeTip()方法会用到,也可以不写在setData里
})
},
// 点提示框, (事件捕获,不能冒泡至关闭方法和 showTip方法,)
clickTip() {
console.log('点击提示框')
},
// 点击其他区域 最外层元素的点击事件
hideTip(e) {
/** 先判断是否来自数组渲染的页面,执行关闭方法*/
/**点击除了显示按钮和提示框之外的其他区域都会冒泡至这个方法,拿到赋好值的数组下标,关闭当前下标的提示框 */
if (this.data.active == 0 && this.data.dataInfo[this.data.tipIdx]) {
this.data.dataInfo[this.data.tipIdx].showTip = false
}
else if (this.data.active !== 0 && this.data.dataInfo[this.data.name] == this.data.currentNameTip ) {
/** 如果来自对象渲染的页面, 并且 name参数和 currentNameTip 参数相等,那么则表示显示了提示框 */
console.log(this.data.dataInfo)
this.data.currentNameTip = '' // 将currentNameTip 设为'', wxml判断 它和name参数不相等,则会关闭弹窗
}
this.setData({
dataInfo: this.data.dataInfo,
currentNameTip: this.data.currentNameTip
}) // 改变视图
},
wxss
wxss(如果觉得样式太长可以只使用 注释下面的几个,分别是按钮样式和提示框样式)wxss
.data-info-box {
width: 100%;
background-color: white;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
}
.data-info {
width: 50%;
height: 272rpx;
position: relative;
background-color: white;
border: 1rpx solid #e8eaf6;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.data-name {
padding-top: 16rpx;
padding-left: 32rpx;
display: flex;
align-items: center;
}
.data {
padding-left: 32rpx;
display: flex;
align-items: flex-end;
}
.dataAdd {
padding-left: 32rpx;
display: flex;
align-items: center;
color: var(--sec-02);
}
.dataAdd>image {
width: 40rpx;
height: 40rpx
}
.unit {
font-weight: normal;
font-size: var(--h4-font-size);
}
/*************如果觉得样式太长可以只使用下面这几个,分别是按钮样式和提示框样式**************/
.doubt {
width: 40rpx;
height: 40rpx;
position: absolute;
top: 16rpx;
right: 24rpx;
}
.tip {
width: 272rpx;
min-height: 100rpx;
color: white;
display: flex;
align-items: center;
justify-content: flex-start;
padding: 8rpx 16rpx;
background: rgba(0, 0, 0, 0.8);
position: absolute;
top: 64rpx;
right: 18rpx;
border-radius: 12rpx;
}
.tip:after {
content:"\00a0";
width:0;
height:0;
display:block;
border-style:solid;
border-width: 8rpx;
border-color:transparent transparent rgba(0, 0, 0, 0.8) transparent;
position:absolute;
z-index: 1;
top: -17rpx;
right: 19rpx;
}
实现效果
数组渲染的页面
对象渲染的页面 (效果其实是一样的)
(实现方式可能有点繁琐,如有错误欢迎指正,若有可以优化的地方或者更好的方式欢迎在评论里发表~谢谢)