Bootstrap

[小程序]实现点击弹出提示框,点击其他区域关闭

[小程序]实现点击弹出提示框(或多个提示框只能显示一个),点击其他区域关闭(带效果图)

如果你的页面是一个数组直接遍历的,那么只看 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}}">&nbsp;</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;
}

实现效果

数组渲染的页面
shuzu

对象渲染的页面 (效果其实是一样的)

在这里插入图片描述

(实现方式可能有点繁琐,如有错误欢迎指正,若有可以优化的地方或者更好的方式欢迎在评论里发表~谢谢)

;